Introduction

What are the most memorable inaugurable address quotes that you can think of off the top of your head? Let me share two of my favorites and see if they are yours too.

“This great nation will endure as it has endured, will revive and will prosper. So, first of all, let me assert my firm belief that the only thing we have to fear is fear itself.” - Franklin D. Roosevelt, March 4, 1933.

“My fellow Americans: Ask not what your country can do for you - ask what you can do for your country. My fellow citizens of the world: Ask not what America will do for you, but what together we can do for the freedom of man.” - John F. Kennedy, Jan. 20, 1961.

Although there are a few very memorable, most inaugural addresses have been long forgotten. This note book will give us a chance to take a closer look at the inaugural speeches of the President of the U.S. (POTUS), but from a data scientic perspective. The scope of this analysis includes 58 Inaugural Speeches of 39 POTUS, which are scrapped from The American Presidentcy Project.

Staging

Overview

  • Step 0 - Install Packages and Load Libraries
  • Step 1 - Data Harvest: Scrap Speech URLs and Read in Speeches
  • Step 2 - Data Processing
  • Step 3 - Build Functions

Step 0 - Install and Load Libraries

### Install and Load Libraries  
# Packages that will be used for this notebook 
packages.used=c("rvest", "xlsx", "tibble", 
                "tm", "tidytext", "dplyr",
                "wordcloud", "qdap", "syuzhet",
                "beeswarm", "RColorBrewer", "sentimentr",  
                 "gplots", "factoextra", "MASS", 
                 "scales", "RANN", "topicmodels")
# Check packages that need to be installed
packages.needed=setdiff(packages.used, 
                        intersect(installed.packages()[,1], 
                                  packages.used))
# Install additional packages
if(length(packages.needed)>0){
  install.packages(packages.needed, dependencies = TRUE,
                   repos='http://cran.us.r-project.org')
}
# Load libraries  
library("rvest")
library("xlsx")
library("tibble")
library("tm")
library("tidytext")
library("MASS")
library("dplyr")
library("wordcloud")
# You may need to run
# sudo ln -f -s $(/usr/libexec/java_home)/jre/lib/server/libjvm.dylib /usr/local/lib
# in order to load qdap
#library(rJava)
library("qdap")
library("syuzhet")
library("beeswarm")
library("RColorBrewer")
library("gplots")
library("scales")
#library("sentimentr")
library("factoextra")
#library("RANN")
#library("topicmodels")
# Obtain speechFuncs functions from the website listed below 
# 'speechFuncs' includes 'f.speechlinks', 'f.plotsent.len', and 'f.smooth.topic'
# https://github.com/TZstatsADS/ADS_Teaching/blob/master/Tutorials/wk2-TextMining/lib/speechFuncs.R
source("../lib/speechFuncs.R")
# Obtain plotstacked functions from the website listed below  
# 'plotstacked' includes 'plot.stacked'
# https://github.com/TZstatsADS/ADS_Teaching/blob/master/Tutorials/wk2-TextMining/lib/plotstacked.R
#source("../lib/plotstacked.R")

This notebook was prepared with the following environmental settings.

print(R.version)

Current working director is

getwd()

Step 1 - Data Harvest

### Data Harvest: Scrap URLs of Inaugural Speeches of POTUS and Read in Speeches 
# Get speeches URLs
main.page <- read_html(x = "http://www.presidency.ucsb.edu/inaugurals.php")
# f.speechlinks is a function for extracting links from the list of speeches. 
inaug.urls <- f.speechlinks(main.page) 
# Remove the last line, irrelevant due to error.  
inaug.urls <- inaug.urls[-nrow(inaug.urls),] 
# Format Date
inaug.urls[,1] <- as.Date(inaug.urls[,1], format="%B %e, %Y")
# Read information file provided "../data/InaugurationInfo.xlsx"
file.path <- "../data/InaugurationInfo.xlsx"
inaug.info <- read.xlsx(file.path, sheetIndex = 1, stringsAsFactors = FALSE)
# Add "Date" and "URLs" columns to inaug.info, named as inaug.list
inaug.list <- add_column(inaug.info, Date = inaug.urls[,1], .after = 4)
inaug.list <- add_column(inaug.list, URLs = inaug.urls[,2])
# Update the "Words" variable for Trump's speech
inaug.list$Words[58] <- 1433 
# Make "Words" as numeric variable
inaug.list$Words <- as.numeric(inaug.list$Words) 
# Make name consistent
inaug.list$President[25] <- "Grover Cleveland" 
inaug.list$President[27] <- "Grover Cleveland" 
inaug.list$File[25] <- "GroverCleveland"
inaug.list$File[27] <- "GroverCleveland"
# Read in each speech  
# Add and stage a 'Fulltext' column to  inaug.list
inaug.list$Fulltext <- NA 
# Indentify the folder "../data/InauguralSpeeches/" 
# where to save each speech as  individual '.txt' file 
folder.path <- "../data/InauguralSpeeches/"
# Loop over each row in inaug.list 
for(i in seq(nrow(inaug.list))) {
  # Read speech from the URL and Store it in variable 'text' 
  text <- read_html(inaug.list$URLs[i]) %>% # Load the page
    html_nodes(".displaytext") %>% # Isloate the text
    html_text() # Get the text 
  # Update the 'Fulltext' column of inaug.list with speech text 
  inaug.list$Fulltext[i] <- text 
  # Create file name of each individual speech
  filename <- paste0(folder.path, 
                     "inaug",
                     inaug.list$File[i], "-", 
                     inaug.list$Term[i], ".txt") 
  # Read text into the '.txt' file created
  sink(file = filename) %>% # Open file to write 
  cat(text)  # Write the file
  sink() # Close the file
}
# Save the inaug.list as '.csv' file under folder "../output/"
write.csv(inaug.list, file = "../output/inaug_list.csv")
#summary(inaug.list)

The scope of this analysis includes 58 Inaugural Speeches of 39 POTUS, who are listed below.

unique(inaug.list$President)
all.File <- unique(inaug.list$File)

Step 2 - Data Processing

### Data Processing  
# Stage a data.frame 'sentence.list'
sentence.list <- NULL
# Loop over each row in inaug.list 
for(i in 1:nrow(inaug.list)){
  # Split each speech into individual sentences 
  sentences <- sent_detect(inaug.list$Fulltext[i], 
                           endmarks = c("?", ".", "!", "|",";"))
  
  if(length(sentences)>0){
    # Count the number of words in each sentence
    word.count <- word_count(sentences)
    # Calculate the presence of eight different emotions in each sentence
    emotions <- get_nrc_sentiment(sentences)
    #colnames(emotions)=paste0("emo.", colnames(emotions))
    # Scale emotions by word.count
    # In case the word counts are zeros, add 0.01 to word.count 
    emotions <- diag(1/(word.count+0.01))%*%as.matrix(emotions)
    # Update sentence.list 
    # Columns include columns of inaug.list, senteces, word.count, emotions(10), sent.id
    # Rows added while looping over each row in inaug.list 
    sentence.list <- rbind(sentence.list, 
                        cbind(
                          # Columns of inaug.list
                          inaug.list[i,-ncol(inaug.list)],
                          # One column vector of sentences in individual speech 
                          sentences=as.character(sentences), 
                          # One column of word count of corresponding sentence
                          word.count,
                          # 8 emotions columns + 2 valence columns (positive/negative)
                          emotions,
                          # Assign consecutive id to each sentence
                          sent.id=1:length(sentences) 
                          )
    )
  }
}
# Clean up sentence.list
# Some non-sentences exist in raw data due to erroneous extra end-of-sentence marks.
sentence.list <- sentence.list%>%filter(!is.na(word.count)) 

Step 3 - Build Functions

Assign Group and Color

# assign.color
# Assign In.File a color correspond to the groups in In.Group
assign.color <- function(In.var, In.Group.var,
                         In.Palette.var="Set1", In.alpha.var=1){
  # In.Group.var is a list of named lists.   
  col.use <- alpha(brewer.pal(length(In.Group.var),
                              In.Palette.var),
                   In.alpha.var)
    for (i in 1:length(In.Group.var)){
      if (In.var %in% unlist(In.Group.var[i])) {return(col.use[i])}
    }
}
# assign.group
# Assign In.var to groups in In.Group.var
assign.group <- function(In.var, In.Group.var){
  # In.Group.var is a list of named lists. 
  for (i in 1:length(In.Group.var)){
    if (In.var %in% unlist(In.Group.var[i])) {return(names(In.Group.var)[i])}
    }
}

Length of Speeches

# Speech.Length.Bar.Plot
# Create Bar Plot for every File in groups of In.Group
# In.Group is a list of named lists 
# In.Term is a list of named lists
Speech.Length.Bar.Plot <- function(In.list=inaug.list,
                                   In.Group, 
                                   In.Term=list("First Term"="1",
                                                "Second Term"="2",
                                                "Third Term"="3",
                                                "Fourth Term"="4"),
                                   In.by.Term=FALSE,
                                   In.Palette="Set1", In.alpha=1){
  var <- In.list%>%
          filter(File%in%unlist(In.Group),
                 Term%in%unlist(In.Term))
  var$File <- factor(var$File)
#  var$FileOrdered <- reorder(var$File, var$Words, mean, order=T)
  if (In.by.Term==FALSE) {
    var$col.id <- unlist(lapply(var$File, assign.color, In.Group, 
                              In.Palette, In.alpha))
    legend.id <- names(In.Group)
  }else{
    var$col.id <- unlist(lapply(var$Term, assign.color, In.Term, 
                              In.Palette, In.alpha))
    legend.id <- names(In.Term)
  }
  
  x <- barplot(var$Words, space = 1,
               col=var$col.id,
               cex.axis = 0.7,
               main = "Number of Words in a Speech" 
              )
  text(cex=0.5, x=x-0.25, y=-1.25, 
       adj=1, srt = 60, xpd = TRUE,
       labels = paste(substr(var$Date,1,4),
                      var$President))
  legend("topright", inset= 0.02, legend =legend.id , 
         text.col = brewer.pal(length(legend.id),
                               In.Palette)[1:length(legend.id)], 
         cex = 0.7, box.lty=2 )
}
# Speech.Length.Box.Plot
# Create Box Plot for groups in In.Group
# In.Group is a list of named lists 
# In.Term is a list of named lists
Speech.Length.Box.Plot <- function(In.list=inaug.list, 
                                   In.Group,
                                   In.Term=list("First Term"="1",
                                                "Second Term"="2",
                                                "Third Term"="3",
                                                "Fourth Term"="4"),
                                   In.by.Term=FALSE,
                                   In.Palette="Set1", In.alpha=1){
  var <- In.list%>%
          filter(File%in%unlist(In.Group),
                 Term%in%unlist(In.Term))
  var$File <- factor(var$File)
#  var$FileOrdered <- reorder(var$File, var$Words, mean, order=T)
  if (In.by.Term==FALSE) {
    var$group.var <- unlist(lapply(var$File, assign.group, In.Group))  
    var$group.var <- factor(var$group.var, names(In.Group))
    legend.id <- names(In.Group)
  }else{
    var$group.var <- unlist(lapply(var$Term, assign.group, In.Term)) 
    var$group.var <- factor(var$group.var, names(In.Term))
    legend.id <- names(In.Term)
  }
  boxplot(var$Words~var$group.var,
          col=alpha(brewer.pal(length(legend.id),In.Palette),
                    In.alpha)[1:length(legend.id)],
          cex.axis = 0.7,
          main = "Number of Words in a Speech" )
}

Length of Sentences

# Bee.Swarm.Plot
# Create Bee Swarm Plot for every File in groups of In.Group
Bee.Swarm.Plot <- function(In.list=sentence.list, In.Group, 
                           In.Palette="Set1", In.alpha=0.4){
  var <- In.list%>%
          filter(File%in%unlist(In.Group))
  var$File <- factor(var$File)
  var$FileOrdered <- reorder(var$File, var$word.count, mean, order=T)
  
  for (i in 1:length(In.Group)){
    if (i==1){
      beeswarm(word.count~FileOrdered, 
             data = var%>%
                    filter(File%in%unlist(In.Group[i])),
             horizontal = TRUE,
             pch=16, col=alpha(brewer.pal(length(In.Group), In.Palette)[i], 
                               In.alpha), 
             cex=0.55, cex.axis=0.55, cex.lab=1,
             spacing=5/nlevels(var$FileOrdered),
             las=2, ylab="", xlab="",
             main="Number of Words in a Sentence")
    } else {
      beeswarm(word.count~FileOrdered, 
             data = var%>%
                    filter(File%in%unlist(In.Group[i])),
             add=TRUE,
             horizontal = TRUE,
             pch=16, col=alpha(brewer.pal(length(In.Group), In.Palette)[i], 
                               In.alpha), 
             cex=0.55, cex.axis=0.55, cex.lab=1,
             spacing=5/nlevels(var$FileOrdered)
             )
    }
  }
  legend("bottomright", inset= 0.02, legend =names(In.Group) , 
         text.col = brewer.pal(length(In.Group),In.Palette)[1:length(In.Group)], 
         cex = 0.7, box.lty=2 )
}
# Bee.Swarm.Plot.Group
# Create Bee Swarm Plot for groups in In.Group
Bee.Swarm.Plot.Group <- function(In.list=sentence.list, In.Group, 
                                 In.Palette="Set1", In.alpha=0.4){
  var <- In.list%>%
          filter(File%in%unlist(In.Group))
  var$File <- factor(var$File)
  var$FileOrdered <- reorder(var$File, var$word.count, mean, order=T)
  var$group.var <- unlist(lapply(var$File, assign.group, In.Group))
  var$group.var <- factor(var$group.var, names(In.Group))
    
  beeswarm(word.count~group.var, 
             data = var%>%filter(File%in%unlist(In.Group)),
             horizontal = TRUE,
             pch=16, col=alpha(brewer.pal(length(In.Group),
                                          In.Palette)[1:length(In.Group)], 
                               In.alpha), 
             cex=0.55, cex.axis=0.55, cex.lab=1,
             spacing=5/nlevels(var$FileOrdered),
             las=2, ylab="", xlab="",
             main="Number of Words in a Sentence")
  legend("bottomright", inset=0.02, legend =names(In.Group) , 
         text.col = brewer.pal(length(In.Group),In.Palette)[1:length(In.Group)], 
         cex = 0.7, box.lty=2 )
  boxplot(var$word.count~var$group.var, horizontal=TRUE, 
          col="#0000ff22", axes=FALSE, add=TRUE)
}
# Sentence.Searcher.Group
# Genenrates shortest or longest sentences of groups in In.Group
# Generate longest sentences if In.Long=True 
Sentence.Searcher.Group <- function(In.list=sentence.list, 
                                    In.Group, 
                                    In.Term=c("1","2","3","4"),
                                    In.Long=TRUE, 
                                    In.min.words=3, In.n.sentences=5){
  df <- c(seq(1:In.n.sentences))
  for (i in 1:length(In.Group)){
    var <-   In.list%>%
             filter(File%in%unlist(In.Group[i]), 
                    Term%in%In.Term,
                    word.count>=In.min.words)%>%
               arrange(desc(word.count))%>%
               select(sentences)
      if (In.Long==TRUE){
        var <- head(var,In.n.sentences)
      } else {
        var <- tail(var,In.n.sentences)
      } 
      if (nrow(var)!=0){
        df<- cbind(df,var) 
      } else {
        df <- cbind(df,c(rep("SYSTEM: No data available in table",In.n.sentences)))
      }
  }
  df<- df[,-1]
  colnames(df)<- names(In.Group)
  df
}

Word Cloud

# Word.Cloud.Group
# Creat Word Cloud for groups in In.Group
Word.Cloud.Group <- function(In.list=inaug.list, In.Group){
  for (i in 1:length(In.Group)){
      var <- In.list%>%
             filter(File%in%unlist(In.Group[i]))
      
      docs <- Corpus(VectorSource(var$Fulltext))
      docs <- tm_map(docs, stripWhitespace) # Eliminate extra whitespace
      docs <- tm_map(docs, content_transformer(tolower)) # Convert to lower case
      docs <- tm_map(docs, removeWords, stopwords("english")) # Remove stopwords
      docs <- tm_map(docs, removeWords, character(0))
      docs <- tm_map(docs, removePunctuation) # Remove punctuations
      #docs <- tm_map(docs, removeNumbers) # Remove numbers
      #docs <- tm_map(docs, stemDocument) # Stem document
      
      tdm <- TermDocumentMatrix(docs)
      tdm.tidy <- tidy(tdm)
      tdm.var <- summarise(group_by(tdm.tidy, term), sum(count))
      
      pal <- c("Reds","Blues","Greens",
               "Purples","Oranges", "Greys")
      
      wordcloud(tdm.var$term, tdm.var$`sum(count)`,
                scale=c(5,0.5),
                max.words=100,
                min.freq=1,
                random.order=FALSE,
                rot.per=0.3,
                use.r.layout=T,
                random.color=FALSE,
                colors=brewer.pal(5, pal[i])
          )
      title(main = names(In.Group)[i], 
            cex.main=1.5,
            col.main=brewer.pal(5, pal[i])[4]
           )
  }
}

Sentimental Analysis

# Sentence.Length.Sentimetal.Plot
# Create Plot of word count of every sentences in an individual speech
Sentence.Length.Sentimetal.Plot <- function(In.list=sentence.list, In.File, In.Term){
  # Top Emotion Value
  In.list$topemotion.v <- apply(select(In.list, anger:positive), 
                                1, max)
  temp <- In.list$topemotion.v
  In.list$topemotion.v[temp<0.05] <- 1
  
  # Top Emotion Location
  In.list$topemotion <- apply(select(In.list, anger:positive), 
                              1, which.max)
  In.list$topemotion[In.list$topemotion.v<0.05] <- 0
  In.list$topemotion <- In.list$topemotion + 1
 
  # Filter and Select from In.list
  df <- In.list%>%
        filter(File==In.File, 
               Term==In.Term)%>%
        select(sent.id, word.count, 
              topemotion, topemotion.v)
  
  # Set color for the plot
  col.use <- brewer.pal(10,"Set3")
  ptcol.use <- alpha(col.use[df$topemotion], 
                     sqrt(sqrt(sqrt(df$topemotion.v))))
  
  # Plot
  plot(df$sent.id, df$word.count, 
       col=ptcol.use,
       type="h" #,ylim=c(-10, max(In.list$word.count))
       )
  title(main =paste(In.File, "Term", In.Term, sep=" "), 
        xlab = "Sentence ID", ylab = "Number of Words")
  legend("topright", inset= 0.02, 
         legend =c("anger","anticipation","disgust", "fear", "joy",
                   "sadness","suprise","trust","negative","positive"), 
         text.col = brewer.pal(10,"Set3"), 
         cex = 0.6, box.lty=2 )
}
# Sentimental.Analysis.Plots 
# Creat Heatmap for correlations and 
# Barplot for average value of emotions for groups in In.Group
Sentimental.Analysis.Plots <- function(In.list=sentence.list, In.Group){
for (i in 1:length(In.Group)){
#  heatmap.2(cor(In.list%>%
#                filter(File%in%unlist(In.Group[i]))%>%
#                select(anger:trust)), 
#          scale = "none", 
#          col = bluered(100), margin=c(6, 6), key=F,
#          trace = "none", density.info = "none")
  emo.means.var <- colMeans(In.list%>%
                         filter(File%in%unlist(In.Group[i]))%>%
                         select(anger:trust)>0.01)
  barplot(emo.means.var[order(emo.means.var)], 
        las=2, col=brewer.pal(8,"Pastel2")[order(emo.means.var)], 
        horiz=T, main=names(In.Group)[i], xlab = "Average Value of Emotions")
}
}
# Sentimental.Sentence.Searcher
# Search for sentences that have the max value of each of the 8 emotions for groups in In.Group
Sentimental.Sentence.Searcher <- function(In.list=sentence.list, 
                                    In.Group, 
                                    In.Term=c("1","2","3","4"), 
                                    In.min.words=3){
  df <- NULL
  for (i in 1:length(In.Group)){
    var <-   In.list%>%
             filter(File%in%unlist(In.Group[i]), 
                    Term%in%In.Term,
                    word.count>=In.min.words)%>%
               select(sentences:trust)
#    var <- as.data.frame(var)
    rst <- as.character(var$sentences[apply(var[,-(1:2)],2,which.max)])
      if (nrow(var)!=0){
        df<- cbind(df,rst) 
      } else {
        df <- cbind(df,c(rep("SYSTEM: No data available in table",8)))
      }
    }
  colnames(df)<- names(In.Group)
  rownames(df) <- c("anger","anticipation","disgust","fear",
                     "joy","sadness","surprise","trust")
  df
}
# Sentimental.Sentence.Kmeans
# Perform k-means clustering for a group In.group
Sentimental.Sentence.KMeans <- function(In.list=sentence.list, 
                                    In.group, 
                                    In.Term=c("1","2","3","4")){
    var <-   In.list%>%
              filter(File%in%unlist(In.group), 
                     Term%in%In.Term)%>%
              group_by(File)%>%
              summarise(anger=mean(anger),
                        anticipation=mean(anticipation),
                        disgust=mean(disgust),
                        fear=mean(fear),
                        joy=mean(joy),
                        sadness=mean(sadness),
                        surprise=mean(surprise),
                        trust=mean(trust)
                        #negative=mean(negative),
                        #positive=mean(positive)
                        )
    var <- as.data.frame(var)
    for(k in 2:9){
      var[,k][is.na(var[,k])] = 0
    }
    rownames(var) <- as.character(var[,1])
    
    presid.summary <- var
    
    km.res <- kmeans(var[,-1], iter.max=200, 4)
    
    return(list(km.res,presid.summary))
    
}

Analysis

Groups of Interest
The following factors were considered in this analysis.

Types of Analysis
For each group of interest, we will perform the following analysis.

Overal and Individual Speeches

Overview

The scope of this analysis includes 58 Inaugural Speeches of 39 POTUS, which are scrapped from The American Presidentcy Project. We will perform some analysis over all these speeches hopefully to learn some new facts about them. We plan to find out the length of these speeches, the length of the sentences used in a speech, and the most commonly used words. Among all these speeches, five inaugural addresses were considered the best of all time, which are Thomas Jefferson’s 1st (1801), Abraham Lincoln’s 2nd (1865), Franklin Roosevelt’s 1st (1933), Franklin Roosevelt’s 2nd (1937), and John F. Kennedy’s (1961). We will take a closer look at them and see if we can find any interesting facts, similaries or differences, among these five speeches, which may contribute to the great success of them. In additionn, the most recent four POTUS may sound more familiar than others to our Millennials. So let’s take their speeches into our consideration as part of the the analysis of individual speecn as well.

Staging

Staging for Word Cloud

# Read in all the speeches  
folder.path <- "../data/InauguralSpeeches/"
speeches <- list.files(path = folder.path, pattern = "*.txt")
#prex.out <- substr(speeches, 6, nchar(speeches)-4)
docs <- Corpus(DirSource(folder.path))
#docs <- Corpus(VectorSource(inaug.list$Fulltext))
# Clean the text document
docs <- tm_map(docs, stripWhitespace) # Eliminate extra whitespace
docs <- tm_map(docs, content_transformer(tolower)) # Convert to lower case
docs <- tm_map(docs, removeWords, stopwords("english")) # Remove stopwords
docs <- tm_map(docs, removeWords, character(0))
docs <- tm_map(docs, removePunctuation) # Remove punctuations
#docs <- tm_map(docs, removeNumbers) # Remove numbers
#docs <- tm_map(docs, stemDocument) # Stem document
tdm.all <- TermDocumentMatrix(docs)
tdm.tidy <- tidy(tdm.all)
tdm.overall <- summarise(group_by(tdm.tidy, term), sum(count))
# Generate `TF-IDF Weighted Document-Term Matrices`  
dtm.all <- DocumentTermMatrix(docs,
                              control = list(weighting = function(x)
                                                weightTfIdf(x,
                                                            normalize =FALSE),
                                             stopwords = TRUE))
dtm.tidy <- tidy(dtm.all)
#dtm.all <- DocumentTermMatrix(docs)
# Convert rownames to filenames
#rownames(dtm.all) <- paste(corpus.list$File, corpus.list$Term, 
#                           corpus.list$sent.id, sep="_")
#rowTotals <- apply(dtm.all , 1, sum) #Find the sum of words in each Document
#dtm.all  <- dtm.all[rowTotals> 0, ]
#corpus.list <- corpus.list[rowTotals>0, ]

Staging for LDA Topic Modeling

# Create corpus list
corpus.list <- sentence.list[2:(nrow(sentence.list)-1), ]
sentence.pre <- sentence.list$sentences[1:(nrow(sentence.list)-2)]
sentence.post <- sentence.list$sentences[3:(nrow(sentence.list)-1)]
corpus.list$snipets <- paste(sentence.pre, corpus.list$sentences, 
                             sentence.post, sep=" ")
rm.rows=(1:nrow(corpus.list))[corpus.list$sent.id==1]
rm.rows=c(rm.rows, rm.rows-1)
corpus.list=corpus.list[-rm.rows, ]
docs <- Corpus(VectorSource(corpus.list$snipets))
#writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
# Clean the text document
docs <- tm_map(docs, stripWhitespace) # Eliminate extra whitespace
docs <- tm_map(docs, content_transformer(tolower)) # Convert to lower case
docs <- tm_map(docs, removeWords, stopwords("english")) # Remove stopwords
docs <- tm_map(docs, removeWords, character(0))
docs <- tm_map(docs, removePunctuation) # Remove punctuations
docs <- tm_map(docs, removeNumbers) # Remove numbers
docs <- tm_map(docs, stemDocument) # Stem document
# Generate `TF-IDF Weighted Document-Term Matrices`  
#dtm.all.lda <- DocumentTermMatrix(docs,
#                          control = list(weighting = function(x)
#                                             weightTfIdf(x,
#                                                         normalize =FALSE),
#                                         stopwords = TRUE))
dtm.all.lda <- DocumentTermMatrix(docs)
# Convert rownames to filenames
rownames(dtm.all.lda) <- paste(corpus.list$File, corpus.list$Term, 
                           corpus.list$sent.id, sep="_")
#Find the sum of words in each Document 
rowTotals <- apply(dtm.all.lda , 1, sum) 
dtm.all.lda  <- dtm.all.lda[rowTotals> 0, ]
corpus.list <- corpus.list[rowTotals>0, ]

Length of Speeches

Below is a summary of the length of all speeches.

summary(inaug.list$Words)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    135    1432    2084    2337    2894    8460 

To get a better sense of the distribution of the number of words in speech, a box-and-whisker plot is created as below. As shown, the speeches of William H. Harrison and William H. Taft spoke were much longer than others.

# Generate bar plot of the length of speeches
bxpdat.speech <- boxplot(inaug.list$Words, xlab="Number of Words in a Speech")
text(x=1.2, y=bxpdat.speech$out, 
     labels=inaug.list$President[which(inaug.list$Words%in%bxpdat.speech$out)], 
     cex=0.7, col="blue")

speech.len.file <- inaug.list$File[order(inaug.list$Words, decreasing =TRUE)]

Below listed the top three POTUS who had the longest and shortest speeches respectively.
The top three POTUS who had the longest speeches are

head(speech.len.file,3)
[1] "WilliamHenryHarrison" "WilliamHowardTaft"    "JamesKPolk"          

The top three POTUS who had the shortest speeches are

tail(speech.len.file,3)
[1] "AbrahamLincoln"     "FranklinDRoosevelt" "GeorgeWashington"  

Length of Sentences

Below is a summary of the length of sentences used in an inaugural speech.

# Summary of the length of sentences
summary(sentence.list$word.count)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00   13.00   20.00   23.92   31.00  124.00 

William Henry Harrison had the longest sentence with 124 words in his speech. Below listed the top three shortest, which have been filter to be longer than three words, and the longest sentenses in all speeches.

sentence.len.file <- sentence.list%>%
                      filter(word.count>=3)%>%
                      arrange(desc(word.count))%>%
                      select(sentences)
tail(sentence.len.file,5)
head(sentence.len.file,5)

A box-and-whisker plot has been created below to help us get a better sense of the distribution of the number of words in a sentence.

word.count.ave <- sentence.list%>%group_by(President)%>%summarise(word.count=round(mean(word.count,0)))
bxpdat.sentence <- boxplot(word.count.ave$word.count, 
                           xlab="Number of Words in a Sentence")
text(x=1.2, y=bxpdat.sentence$out, cex=0.5, col="blue")

Word Cloud

The text with larger in size and darker in color as shown in the words cloud below are the words that appeared more often in all inaugural speeches. With no surprise , the most commonly used words in all speeches include “will”, “government”, and “people”.

set.seed(123)
wordcloud(tdm.overall$term, tdm.overall$`sum(count)`,
          scale=c(5,0.5),
          max.words=100,
          min.freq=1,
          random.order=FALSE,
          rot.per=0.3,
          use.r.layout=T,
          random.color=FALSE,
          colors=brewer.pal(5,"Blues")
          ) 
title(main="What Are The Most Common Words in Inaugural Speeches?", cex.main=0.9)

An interactive wordcloud has been created to help us picture the most frequent words used in each individual speech. This could be used to find the wordcloud of each individual speech as well as to compare between two different speeches. We will use this interactive wordcloud to indentify the most commonly used words for the most famous iaugural speeches as well as the iaugural speeches given by the four most recent presidents.

library(shiny)

shinyApp(
    ui = fluidPage(
      fluidRow(style = "padding-bottom: 20px;",
        column(4, selectInput('speech1', 'Speech 1', 
                              speeches, selected=speeches[5])),
        column(4, selectInput('speech2', 'Speech 2', 
                              speeches, selected=speeches[9])),
        column(4, sliderInput('nwords', 'Number of words', 3, 
                              min = 20, max = 200, value=100, step = 20))
      ),
      fluidRow(
        plotOutput('wordclouds', height = "400px")
      ),

      fluidRow( 
        actionButton("close","Close window",
                     onclick = "setTimeout(function(){window.close();},500);")
        )
    ),
    
    server = function(input, output, session) {
      # Combine the selected variables into a new data frame
      selectedData <- reactive({
        list(dtm.term1=dtm.tidy$term[dtm.tidy$document==
                                     as.character(which(speeches == input$speech1))],
             dtm.count1=dtm.tidy$count[dtm.tidy$document==
                                       as.character(which(speeches == input$speech1))],
             dtm.term2=dtm.tidy$term[dtm.tidy$document==
                                     as.character(which(speeches == input$speech2))],
             dtm.count2=dtm.tidy$count[dtm.tidy$document==
                                       as.character(which(speeches == input$speech2))])
      })
      
      output$wordclouds <- renderPlot(height = 400, {
        par(mfrow=c(1,2), mar = c(0, 0, 3, 0))
        wordcloud(selectedData()$dtm.term1, 
                  selectedData()$dtm.count1,
              scale=c(4,0.5),
              max.words=input$nwords,
              min.freq=1,
              random.order=FALSE,
              rot.per=0.3,
              use.r.layout=T,
              random.color=FALSE,
              colors=brewer.pal(5,"Blues"), 
            main=input$speech1)
        wordcloud(selectedData()$dtm.term2, 
                  selectedData()$dtm.count2,
              scale=c(4,0.5),
              max.words=input$nwords,
              min.freq=1,
              random.order=FALSE,
              rot.per=0.3,
              use.r.layout=T,
              random.color=FALSE,
              colors=brewer.pal(5,"Blues"), 
            main=input$speech2)
      })
      
      observe({
        if (input$close>0) stopApp()
#        js$closeWindow()
    })
    },
    options = list(height = 600)
)

Sentimental Analysis

For the five inaugural addresses which are considered the best across of all time, we can see how their emotion changed over the speech, as shown in the plot below.

par(mar=c(2,2,2,1), mfrow=c(7,1))
Sentence.Length.Sentimetal.Plot(sentence.list,"ThomasJefferson",1)
Sentence.Length.Sentimetal.Plot(sentence.list,"AbrahamLincoln",2)
Sentence.Length.Sentimetal.Plot(sentence.list,"FranklinDRoosevelt",1)
Sentence.Length.Sentimetal.Plot(sentence.list,"FranklinDRoosevelt",2)
Sentence.Length.Sentimetal.Plot(sentence.list,"JohnFKennedy",1)

For the most recent four POTUS, who may sound more familiar than others to our Millennials, let’s take a closer look at how their emotion changed across over the speeches. Below shows the analysis of speeches from both the first and second terms, while to note that President Trump is currently in his first term.

par(mar=c(2,2,2,1), mfrow=c(7,1))
Sentence.Length.Sentimetal.Plot(sentence.list,"WilliamJClinton",1)
Sentence.Length.Sentimetal.Plot(sentence.list,"WilliamJClinton",2)
Sentence.Length.Sentimetal.Plot(sentence.list,"GeorgeWBush",1)
Sentence.Length.Sentimetal.Plot(sentence.list,"GeorgeWBush",2)
Sentence.Length.Sentimetal.Plot(sentence.list,"BarackObama",1)
Sentence.Length.Sentimetal.Plot(sentence.list,"BarackObama",2)
Sentence.Length.Sentimetal.Plot(sentence.list,"DonaldJTrump",1)

Topic Modeling

# Run LDA (Latent Dirichlet allocation)
# Set parameters for Gibbs sampling
burnin <- 4000 # Drop the first 4000 samples
iter <- 2000
thin <- 500 # Pick only every 500 guesses
seed <-list(2003,5,63,100001,765)
nstart <- 5
best <- TRUE # Sample with the largest posterior distribution 
k <- 8 # Number of topics
# Run LDA using Gibbs sampling
ldaOut.all <-LDA(dtm.all.lda, k, method="Gibbs", control=list(nstart=nstart, 
                                                 seed = seed, best=best,
                                                 burnin = burnin, iter = iter, 
                                                 thin=thin))
# Write out results
# Docs to topics
ldaOut.topics.all <- as.matrix(topics(ldaOut.all))
table(c(1:k, ldaOut.topics.all))

  1   2   3   4   5   6   7   8 
770 750 982 658 788 481 578 532 
write.csv(ldaOut.topics.all,file=paste("../output/LDAGibbs ",
                                   k," DocsToTopics.csv", sep = ''))
# Top 10 terms in each topic
ldaOut.terms.all <- as.matrix(terms(ldaOut.all,10))
write.csv(ldaOut.terms.all,file=paste("../output/LDAGibbs ",
                                  k," TopicsToTerms.csv", sep = ''))
# Probabilities associated with each topic assignment
topicProbabilities.all <- as.data.frame(ldaOut.all@gamma)
write.csv(topicProbabilities.all,file=paste("../output/LDAGibbs ",
                                        k," TopicProbabilities.csv", sep = ''))
terms.beta <- ldaOut.all@beta
terms.beta <- scale(terms.beta)
topics.terms <- NULL
for(i in 1:k){
  topics.terms.all <- rbind(topics.terms, 
                        ldaOut.all@terms[order(terms.beta[i,], 
                                           decreasing = TRUE)[1:10]])
}
#topics.terms.all
ldaOut.terms.all[1:3,]
     Topic 1  Topic 2 Topic 3 Topic 4 Topic 5 Topic 6  Topic 7  Topic 8  
[1,] "govern" "war"   "world" "law"   "time"  "will"   "peopl"  "countri"
[2,] "state"  "great" "peac"  "upon"  "year"  "nation" "free"   "duti"   
[3,] "power"  "secur" "new"   "shall" "now"   "can"    "nation" "may"    

Based on the most popular terms and the most salient terms for each topic, we assign a hashtag to each topic, which are “Goverment”, “War”, “World”, “Law”, “Time”, “Nation”, “People”, are “Country”.

topics.hash <- c("Goverment", "War", "World", "Law", 
                 "Time", "Nation", "People", "Country")
print(topics.hash)
[1] "Goverment" "War"       "World"     "Law"       "Time"      "Nation"    "People"   
[8] "Country"  
corpus.list$ldatopic <- as.vector(ldaOut.topics.all)
corpus.list$ldahash <- topics.hash[ldaOut.topics.all]
colnames(topicProbabilities.all) <- topics.hash
corpus.list.df <- cbind(corpus.list, topicProbabilities.all)

We can use Word Cloud to visualize the top topics as shown below.

set.seed(123)
wordcloud(corpus.list.df$ldahash,
          scale=c(5,0.5),
          max.words=100,
          min.freq=1,
          random.order=FALSE,
          rot.per=0.3,
          use.r.layout=T,
          random.color=FALSE,
          colors=brewer.pal(5,"Blues")
          )

Number of Terms Served

Overview

Summary of Group of Interest

Number of Terms Served Number of Presidents
One Term 22
Two Terms 16
Four Terms 1

This analysis has primarily focused on presidents who served one or two terms. The analysis of the speeches of Franklin Roosevelt, who was the only POTUS who served more than two terms, may be incorporated in the furture.

Staging

### Number of Terms Served
# Summary of groups by the number of terms served
#inaug.list%>%
#  group_by(Term)%>%
#  summarise(Count = n_distinct(President))
# Identify 'File' for each group 
four.terms <- unique(inaug.list$File[inaug.list$Term>2])
twoplus.terms <- inaug.list$File[inaug.list$Term==2]
two.terms <- twoplus.terms[twoplus.terms != four.terms]
one.term <- inaug.list$File[!(inaug.list$File%in%twoplus.terms)]
# Create group and group name
TermGroup <- list("One Term"=one.term, "Two Terms"=two.terms, "Four Terms"=four.terms)
#TermGroup.name <- "Number of Terms Served"

POTUS are assigned into three groups by the number of terms they have served.

  • 22 POTUS who have served only one term:
TermGroup$`One Term`
 [1] "JohnAdams"            "JohnQuincyAdams"      "MartinvanBuren"      
 [4] "WilliamHenryHarrison" "JamesKPolk"           "ZacharyTaylor"       
 [7] "FranklinPierce"       "JamesBuchanan"        "RutherfordBHayes"    
[10] "JamesGarfield"        "BenjaminHarrison"     "TheodoreRoosevelt"   
[13] "WilliamHowardTaft"    "WarrenGHarding"       "CalvinCoolidge"      
[16] "HerbertHoover"        "HarrySTruman"         "JohnFKennedy"        
[19] "LyndonBJohnson"       "JimmyCarter"          "GeorgeBush"          
[22] "DonaldJTrump"        
  • 16 POTUS who have served two terms:
TermGroup$`Two Terms`
 [1] "GeorgeWashington"  "ThomasJefferson"   "JamesMadison"      "JamesMonroe"      
 [5] "AndrewJackson"     "AbrahamLincoln"    "UlyssesSGrant"     "GroverCleveland"  
 [9] "WilliamMcKinley"   "WoodrowWilson"     "DwightDEisenhower" "RichardNixon"     
[13] "RonaldReagan"      "WilliamJClinton"   "GeorgeWBush"       "BarackObama"      
  • 1 POTUS who has served more than two terms:
TermGroup$`Four Terms`
[1] "FranklinDRoosevelt"

Length of Speeches

By looking the bar plots and box-whisker plots of the number of words used in a speech, we can see that the length of the inaugural speeches of the POTUS who served only one term is apparently longer than that of the other POTUS who served more than one term.

par(mar=c(4.5,3,2,3), mfrow=c(1,2))
# Create bar plot of each speech
Speech.Length.Bar.Plot(inaug.list,TermGroup)
# Create box plot of each speech by group 
Speech.Length.Box.Plot(inaug.list,TermGroup)

For POTUS who served two terms, we compared the length of speeches of their first term and sencond term. As shown in the plots, the length of speeches of their two terms are generally consistent, except for that of Abraham Lincoln’s - Abraham Lincoln’s sencond term speepch is a lot shorter than his first.

par(mar=c(4.5,3,2,3), mfrow=c(1,2))
# Create bar plot of each speech
Speech.Length.Bar.Plot(inaug.list,TermGroup[2],
                       In.Term =list("First Term"="1",
                                     "Second Term"="2"),
                       In.by.Term = TRUE)
# Create box plot of each speech by group 
Speech.Length.Box.Plot(inaug.list,TermGroup[2],
                       In.Term = list("First Term"="1",
                                     "Second Term"="2"),
                       In.by.Term = TRUE)

Length of Sentences

By looking the bee swarm plots and box-whisker plots of the number of words used in a sentence, we can see that the length of sentences used by POTUS who served only one term is generally a bit longer than those of other POTUS who served more than one term.

par(mar=c(4.5,5.5,2,1), mfrow=c(1,2))
Bee.Swarm.Plot(sentence.list,TermGroup)
Bee.Swarm.Plot.Group(sentence.list,TermGroup)

Below listed the shortest and longest sentenses used in these speeches for each goup.

Sentence.Searcher.Group(sentence.list,TermGroup,
                        In.Term=c("1","2","3","4"),In.Long=FALSE, 
                        In.min.words=3, In.n.sentences=5)
Sentence.Searcher.Group(sentence.list,TermGroup,
                        In.Term=c("1","2","3","4"),In.Long=TRUE, 
                        In.min.words=3, In.n.sentences=5)
par(mar=c(4.5,5.5,2,1), mfrow=c(1,2))
# Create bee swarm plot for 'Two Terms' group
ii.var <- sentence.list%>%
            filter(File%in%unlist(TermGroup[2]),
                   Term%in%c(1,2))
ii.var$File <- factor(ii.var$File)
ii.var$FileOrdered <- reorder(ii.var$File, ii.var$word.count, mean, order=T)
  
beeswarm(word.count~FileOrdered, 
             data = ii.var%>%filter(Term==1),
             horizontal = TRUE,
             pch=16, col=alpha(brewer.pal(9, "Blues")[4], 1), 
             cex=0.55, cex.axis=0.55, cex.lab=1,
             spacing=5/nlevels(ii.var$FileOrdered),
             las=2, ylab="", xlab="",
             main="Number of Words in a Sentence")
beeswarm(word.count~FileOrdered, 
             data = ii.var%>%filter(Term==2),
             add=TRUE,
             horizontal = TRUE,
             pch=16, col=alpha(brewer.pal(9, "Blues")[7], 1), 
             cex=0.55, cex.axis=0.55, cex.lab=1,
             spacing=5/nlevels(ii.var$FileOrdered))
legend("bottomright", inset= 0.02, legend =c("First Term", "Second Term"), 
         text.col = brewer.pal(9,"Blues")[c(4,7)], 
         cex = 0.7, box.lty=2 )
beeswarm(word.count~Term, 
             data = ii.var,
             horizontal = TRUE,
             pch=16, col=alpha(brewer.pal(9,
                                          "Blues")[c(4,7)], 
                               0.4), 
             cex=0.55, cex.axis=0.55, cex.lab=1,
             spacing=5/nlevels(ii.var$FileOrdered),
             las=2, ylab="", xlab="",
             main="Number of Words in a Sentence")
  legend("bottomright", inset=0.02, legend =c("First Term", "Second Term") , 
         text.col = brewer.pal(9,"Blues")[c(4,7)], 
         cex = 0.7, box.lty=2 )
  boxplot(ii.var$word.count~ii.var$Term, horizontal=TRUE, 
          col="#0000ff22", axes=FALSE, add=TRUE)

For POTUS who served two terms, we compared speeches of their first term and sencond term. From the plots, we can see that speeches of their two terms are generally consistent. Below listed the shortest and longest sentenses in the speeches of the presidents who served two terms.

short.first <- Sentence.Searcher.Group(sentence.list,TermGroup,c("1"),FALSE)[,2]
short.second <- Sentence.Searcher.Group(sentence.list,TermGroup,c("2"),FALSE)[,2]
short.two.terms <- data.frame(short.first, short.second)
colnames(short.two.terms) <- c("First Term", "Second Term")
short.two.terms
long.first <- Sentence.Searcher.Group(sentence.list,TermGroup,c("1"),TRUE)[,2]
long.second <- Sentence.Searcher.Group(sentence.list,TermGroup,c("2"),TRUE)[,2]
long.two.terms <- data.frame(long.first, long.second)
colnames(long.two.terms) <- c("First Term", "Second Term")
long.two.terms

Word Cloud

The text with larger in size and darker in color as shown in the words cloud below are the words that appeared more often in the speeches. As we can see, the most frequent words used by the POTUS of these three groups are the same, which inclue “will”, “people”, and “government”.

par(mar=c(1,1,1,1), mfrow=c(1,length(TermGroup)))
set.seed(123)
Word.Cloud.Group(inaug.list,TermGroup)

For POTUS who served two terms, as shown the in the word cloud below, the most frequently used words are very similar between their first term and sencond term.

par(mar=c(1,1,1,1), mfrow=c(1,2))
set.seed(123)
for (i in 1:2){
  ii.var <- inaug.list%>%
             filter(File%in%unlist(TermGroup[2]))
  ii.docs <- Corpus(VectorSource(ii.var$Fulltext))
  ii.docs <- tm_map(ii.docs, stripWhitespace) # Eliminate extra whitespace
  ii.docs <- tm_map(ii.docs, content_transformer(tolower)) # Convert to lower case
  ii.docs <- tm_map(ii.docs, removeWords, stopwords("english")) # Remove stopwords
  ii.docs <- tm_map(ii.docs, removeWords, character(0))
  ii.docs <- tm_map(ii.docs, removePunctuation) # Remove punctuations
  #ii.docs <- tm_map(ii.docs, removeNumbers) # Remove numbers
  #ii.docs <- tm_map(ii.docs, stemDocument) # Stem document
      
  ii.tdm <- TermDocumentMatrix(ii.docs)
  ii.tdm.tidy <- tidy(ii.tdm)
  ii.tdm.var <- summarise(group_by(ii.tdm.tidy, term), sum(count))
      
  wordcloud(ii.tdm.var$term, ii.tdm.var$`sum(count)`,
                scale=c(5,0.5),
                max.words=100,
                min.freq=1,
                random.order=FALSE,
                rot.per=0.3,
                use.r.layout=T,
                random.color=FALSE,
                colors=brewer.pal(5, "Blues")
          )
  title(main = c("First Term","Second Term")[i], 
            cex.main=1.5,
            col.main=brewer.pal(5, "Blues")[4])
}

Sentimental Analysis

As shown in the bar plots below, the overall emotion pattern are very similar for these three groups.

par(mar=c(4.5,5.5,2,1), mfrow=c(length(TermGroup),1))
# Create barplot of average value of emotions for each group
Sentimental.Analysis.Plots(sentence.list,TermGroup)

We also compared the first term and second term speeches of the POTUS who served two terms. As shown below, the fear level increases but the sadness level decreases from the first term to the second term, while all other emotions seemed at the same level as the the first term.

par(mar=c(4.5,5.5,2,1), mfrow=c(2,1))
# Creat Heatmap for correlations and 
# Barplot for average value of emotions for groups in In.Group
#  heatmap.2(cor(sentence.list%>%
#                filter(File%in%unlist(TermGroup[2]))%>%
#                select(anger:trust)), 
#          scale = "none", 
#          col = bluered(100), margin=c(6, 6), key=F,
#          trace = "none", density.info = "none")
  i.emo.means.var <- colMeans(sentence.list%>%
                         filter(File%in%unlist(TermGroup[2]),Term==1)%>%
                         select(anger:trust)>0.01)
  ii.emo.means.var <- colMeans(sentence.list%>%
                         filter(File%in%unlist(TermGroup[2]),Term==2)%>%
                         select(anger:trust)>0.01)
  barplot(i.emo.means.var[order(i.emo.means.var)], 
        las=2, col=brewer.pal(8,"Pastel2")[order(i.emo.means.var)], 
        horiz=T, main="First Term", xlab = "Average Value of Emotions")
  barplot(ii.emo.means.var[order(ii.emo.means.var)], 
        las=2, col=brewer.pal(8,"Pastel2")[order(ii.emo.means.var)], 
        horiz=T, main="Second Term", xlab = "Average Value of Emotions")

Trying to better understand the difference, we have indentified the most influential sentences that drove the fear and sadness level for both terms.

i.emo.var <- sentence.list%>%
                         filter(File%in%unlist(TermGroup[2]),Term==1)%>%
                         select(sentences, fear,sadness)
ii.emo.var <- sentence.list%>%
                         filter(File%in%unlist(TermGroup[2]),Term==2)%>%
                         select(sentences, fear,sadness)
i.fear <- head(i.emo.var%>%arrange(desc(fear))%>%select(sentences))
ii.fear <- head(ii.emo.var%>%arrange(desc(fear))%>%select(sentences))
i.sadness <- head(i.emo.var%>%arrange(desc(sadness))%>%select(sentences))
ii.dadness <- head(ii.emo.var%>%arrange(desc(sadness))%>%select(sentences))
i.emo.df <- cbind(i.fear,ii.fear,i.sadness,ii.dadness)
colnames(i.emo.df) <- c("First Term Fear","Second Term Fear",
                        "Fist Term Sadness", "Second Term Sadness")
i.emo.df

Below are the emotionally charged sentences of each of these three groups.

# What are the emotionally charged sentences?  
Sentimental.Sentence.Searcher(sentence.list,TermGroup,c("1","2","3","4"))
             One Term                                    
anger        "Crime is increasing."                      
anticipation "God bless you."                            
disgust      "we do not hate;"                           
fear         "We know that self-government is difficult."
joy          "God bless you."                            
sadness      "There are the homeless, lost and roaming." 
surprise     "in all things, generosity."                
trust        "God bless you."                            
             Two Terms                                                     
anger        "They are inconvenient."                                      
anticipation "to the elevation of labor;"                                  
disgust      "With riches has come inexcusable waste."                     
fear         "government is the problem."                                  
joy          "to the elevation of labor;"                                  
sadness      "Dark pictures and gloomy forebodings are worse than useless."
surprise     "Hope maketh not ashamed."                                    
trust        "freedom of religion;"                                        
             Four Terms                                            
anger        "Happiness lies not in the mere possession of money;" 
anticipation "We shall strive for perfection."                     
disgust      "Yet our distress comes from no failure of substance."
fear         "These are the lines of attack."                      
joy          "Have we found our happy valley?"                     
sadness      "We are stricken by no plague of locusts."            
surprise     "power to do good."                                   
trust        "Have we found our happy valley?"                     

For the POTUS who have served two terms, below are the emotionally charged sentences of their first term speeches.

Sentimental.Sentence.Searcher(sentence.list,TermGroup[2],c("1"))
             Two Terms                                
anger        "Americans deserve better."              
anticipation "God bless you, and thank you."          
disgust      "With riches has come inexcusable waste."
fear         "government is the problem."             
joy          "God bless you, and thank you."          
sadness      "Unanimity is impossible."               
surprise     "I know America's youth."                
trust        "freedom of religion;"                   

And below listed the emotionally charged sentences of their second term speeches.

Sentimental.Sentence.Searcher(sentence.list,TermGroup[2],c("2"))
             Two Terms                                                     
anger        "They are inconvenient."                                      
anticipation "to the elevation of labor;"                                  
disgust      "They are inconvenient."                                      
fear         "They fuel the fanaticism of terror."                         
joy          "to the elevation of labor;"                                  
sadness      "Dark pictures and gloomy forebodings are worse than useless."
surprise     "Hope maketh not ashamed."                                    
trust        "to the elevation of labor;"                                  

We perform the k-means clustering for the two groups, POTUS who served one term or two terms. The results can be visualized as shown below.

par(mar=c(1,1,1,1), mfrow=c(1,2))
# One Term
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,TermGroup[1],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

# Two  Terms
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,TermGroup[2],c("1","2","3","4"))

## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

For POTUS who served two terms, it is interesting to see there are a lot differeces in the clustering results between their first term and second term, as shown below.

par(mar=c(4.5,5.5,2,1), mfrow=c(2,1))
# First Term
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,TermGroup[2],c("1"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

# Second  Terms
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,TermGroup[2],c("2"))

## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

Political Party

Overview

Political Party Number of Presidents
Republican 17
Democratic 14

Staging

### Political Party
# Summary of groups by politial party
inaug.list%>%
  group_by(Party)%>%
  summarise(Count = n_distinct(President))
# Identify 'File' for each group 
Democrats <- inaug.list$File[inaug.list$Party=="Democratic"]
Republicans <- inaug.list$File[inaug.list$Party=="Republican"]
# Create group and group name
PartyGroup <- list("Republicans"=Republicans, "Democrats"=Democrats)
#PartyGroup.name <- "Political Party"

31 POTUS are assigned into 2 groups by the political party they served, i.e., Republican vs. Democratic. The rest of the POTUS will not be in scope of the analysis in terms of the political party they served.

  • 17 POTUS who are Republicans:
PartyGroup$Republicans
 [1] "AbrahamLincoln"    "AbrahamLincoln"    "UlyssesSGrant"     "UlyssesSGrant"    
 [5] "RutherfordBHayes"  "JamesGarfield"     "BenjaminHarrison"  "WilliamMcKinley"  
 [9] "WilliamMcKinley"   "TheodoreRoosevelt" "WilliamHowardTaft" "WarrenGHarding"   
[13] "CalvinCoolidge"    "HerbertHoover"     "DwightDEisenhower" "DwightDEisenhower"
[17] "RichardNixon"      "RichardNixon"      "RonaldReagan"      "RonaldReagan"     
[21] "GeorgeBush"        "GeorgeWBush"       "GeorgeWBush"       "DonaldJTrump"     
  • 14 POTUS who are Democrats:
PartyGroup$Democrats
 [1] "AndrewJackson"      "AndrewJackson"      "MartinvanBuren"     "JamesKPolk"        
 [5] "FranklinPierce"     "JamesBuchanan"      "GroverCleveland"    "GroverCleveland"   
 [9] "WoodrowWilson"      "WoodrowWilson"      "FranklinDRoosevelt" "FranklinDRoosevelt"
[13] "FranklinDRoosevelt" "FranklinDRoosevelt" "HarrySTruman"       "JohnFKennedy"      
[17] "LyndonBJohnson"     "JimmyCarter"        "WilliamJClinton"    "WilliamJClinton"   
[21] "BarackObama"        "BarackObama"       

Length of Speeches

par(mar=c(4.5,3,2,3), mfrow=c(1,2))
# Create bar plot of each speech
Speech.Length.Bar.Plot(inaug.list,PartyGroup)
# Create box plot of each speech by group 
Speech.Length.Box.Plot(inaug.list,PartyGroup)

Length of Sentences

par(mar=c(4.5,5.5,2,1), mfrow=c(1,2))
Bee.Swarm.Plot(sentence.list,PartyGroup)
Bee.Swarm.Plot.Group(sentence.list,PartyGroup)

Below listed the shortest and longest sentenses in the speech.

Sentence.Searcher.Group(sentence.list,PartyGroup,c("1","2","3","4"),FALSE)
Sentence.Searcher.Group(sentence.list,PartyGroup,c("1","2","3","4"),TRUE)

Word Cloud

par(mar=c(4.5,5.5,2,1), mfrow=c(1,length(PartyGroup)))
set.seed(123)
Word.Cloud.Group(inaug.list,PartyGroup)

Sentimental Analysis

As shown, the fear level of speeches of Democrats is higher than that of Republicans, while all other emotions seemed to be at the same level as them.

par(mar=c(4.5,5.5,2,1), mfrow=c(length(PartyGroup),1))
# Create barplot of average value of emotions for each group
Sentimental.Analysis.Plots(sentence.list,PartyGroup)

We would like to look into it closer and try to indentify the most influced sentences that drove the difference.

i.emo.var <- sentence.list%>%
                         filter(File%in%unlist(PartyGroup[1]))%>%
                         select(sentences, fear)
ii.emo.var <- sentence.list%>%
                         filter(File%in%unlist(PartyGroup[2]))%>%
                         select(sentences, fear)
i.fear <- head(i.emo.var%>%arrange(desc(fear))%>%select(sentences))
ii.fear <- head(ii.emo.var%>%arrange(desc(fear))%>%select(sentences))
i.emo.df <- cbind(i.fear,ii.fear)
colnames(i.emo.df) <- c("Republicans Fear","Democrats Fear")
i.emo.df

Below are the emotionally charged sentences of each of these two groups.

# What are the emotionally charged sentences?  
Sentimental.Sentence.Searcher(sentence.list,PartyGroup,c("1","2","3","4"))

We perform the k-means clustering for the two groups. The results can be visualized as shown below.

par(mar=c(4.5,5.5,2,1), mfrow=c(2,1))
# Reputlican
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,PartyGroup[1],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

# Democratic
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,PartyGroup[2],c("1","2","3","4"))

## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

Educational Background

Overview

Educational Background Number of Presidents
No Colledge 9
Undergraduate 21
Advanced Degree 7

Staging

Educational backgound of POTUS were obtained from the following websites:

### Educational Background
# Indentify educational background of each POTUS 
NoColledge <- c("GeorgeWashington", "JamesMonroe", "AndrewJackson",
                "MartinvanBuren", "ZacharyTaylor", "AbrahamLincoln",
                "GroverCleveland", "WilliamMcKinley", "HarrySTruman")
Undergrad <- c("ThomasJefferson","JamesMadison", "JohnQuincyAdams",
               "JamesKPolk", "FranklinPierce", "JamesBuchanan", 
               "UlyssesSGrant", "JamesGarfield", "BenjaminHarrison", 
               "TheodoreRoosevelt", "WarrenGHarding", "CalvinCoolidge", 
               "HerbertHoover", "FranklinDRoosevelt", "DwightDEisenhower", 
               "JohnFKennedy", "LyndonBJohnson", "JimmyCarter", 
               "RonaldReagan", "GeorgeBush", "DonaldJTrump")
MA.MS <- c("JohnAdams")
MBA <- c("GeorgeWBush")
Law <- c("RutherfordBHayes", "RichardNixon", "WilliamJClinton", "BarackObama")
Doctorate <- c("WoodrowWilson")

Advanced.Degree <- c(MA.MS, MBA, Law, Doctorate) 

# Group POTUS into three categories and Create group and group name
EducationGroup <- list("No Colledge"=NoColledge,
                        "Undergraduate"=Undergrad,
                        "Advanced Degree"=Advanced.Degree)
#EducationGroup.name <- "Educational Background"

#summary(EducationGroup)[,1]

POTUS are assigned into three groups based on their highest education level.

  • 9 POTUS without a colledge degree include:
EducationGroup$`No Colledge`
  • 21 POTUS with only undergraduate degree include:
EducationGroup$Undergraduate
  • 7 POTUS with more advanced degree include:
EducationGroup$`Advanced Degree`

Length of Speeches

par(mar=c(4.5,3,2,3), mfrow=c(1,2))

# Create bar plot of each speech
Speech.Length.Bar.Plot(inaug.list,EducationGroup)
# Create box plot of each speech by group 
Speech.Length.Box.Plot(inaug.list,EducationGroup)

Length of Sentences

par(mar=c(4.5,5.5,2,1), mfrow=c(1,2))

Bee.Swarm.Plot(sentence.list,EducationGroup)
Bee.Swarm.Plot.Group(sentence.list,EducationGroup)

Below listed the shortest and longest sentenses in the speech.

Sentence.Searcher.Group(sentence.list,EducationGroup,c("1","2","3","4"),FALSE)
Sentence.Searcher.Group(sentence.list,EducationGroup,c("1","2","3","4"),TRUE)

Word Cloud

par(mar=c(4.5,5.5,2,1), mfrow=c(1,length(EducationGroup)))
set.seed(123)

Word.Cloud.Group(inaug.list,EducationGroup)

Sentimental Analysis

par(mar=c(4.5,5.5,2,1), mfrow=c(length(EducationGroup),1))
# Create barplot of average value of emotions for each group
Sentimental.Analysis.Plots(sentence.list,EducationGroup)

Below are the emotionally charged sentences of each of these two groups.

# What are the emotionally charged sentences?  
Sentimental.Sentence.Searcher(sentence.list,EducationGroup,c("1","2","3","4"))

We performed the k-means clustering for the two groups. The results can be visualized as shown below.

par(mar=c(4.5,5.5,2,1), mfrow=c(2,1))
# No Colledge Degree 
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,EducationGroup[1],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

# Undergraduate Degree Only
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,EducationGroup[2],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

# Advenced Degree
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,EducationGroup[3],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

Career Prior to Politics

Overview

Career Prior to Politics Number of Presidents
Lawyer 21
Military Leader 3
Farmer 3
Educator 2
Businessperson 3

Staging

Career backgound of POTUS were obtained from the following website:

### Career Prior to Politics  
# Indentify career background of each POTUS 
lawyer <- c("JohnAdams", "ThomasJefferson", "JamesMadison", 
            "JamesMonroe", "JohnQuincyAdams", "AndrewJackson", 
            "MartinvanBuren", "JamesKPolk", "FranklinPierce", 
            "JamesBuchanan", "AbrahamLincoln", "RutherfordBHayes", 
            "JamesGarfield", "GroverCleveland", "BenjaminHarrison", 
            "WilliamMcKinley", "CalvinCoolidge", "FranklinDRoosevelt", 
            "RichardNixon", "WilliamJClinton", "BarackObama")
military.leader <- c("ZacharyTaylor", "UlyssesSGrant", "DwightDEisenhower")
farmer <- c("GeorgeWashington", "HarrySTruman", "JimmyCarter")
businessperson <- c("GeorgeBush", "GeorgeWBush", "DonaldJTrump")
educator <- c("WoodrowWilson", "LyndonBJohnson")
# Group POTUS into five categories and Create group and group name
CareerGroup <- list("Lawyer"=lawyer, 
               "Military Learder"=military.leader,
               "Farmer"=farmer,
               "Educator"=educator, 
               "Businessperson"=businessperson)
#CareerGroup.name <- "Career Prior to Politics"
#summary(CareerGroup)[,1]

POTUS are assigned into five groups based on their career pior to politics.

  • 21 POTUS were lawywers include:
CareerGroup$Lawyer
  • 3 POTUS were military leaders:
CareerGroup$`Military Learder`
  • 3 POTUS were farmers:
CareerGroup$Farmer
  • 2 POTUS were educators:
CareerGroup$Educator
  • 3 POTUS were businesspersons:
CareerGroup$Businessperson

Length of Speeches

par(mar=c(4.5,3,2,3), mfrow=c(1,2))

# Create bar plot of each speech
Speech.Length.Bar.Plot(inaug.list,CareerGroup)
# Create box plot of each speech by group 
Speech.Length.Box.Plot(inaug.list,CareerGroup)

Length of Sentences

par(mar=c(4.5,5.5,2,1), mfrow=c(1,2))

Bee.Swarm.Plot(sentence.list,CareerGroup)
Bee.Swarm.Plot.Group(sentence.list,CareerGroup)

Below listed the shortest and longest sentenses in the speech.

Sentence.Searcher.Group(sentence.list,CareerGroup,c("1","2","3","4"),FALSE)
Sentence.Searcher.Group(sentence.list,CareerGroup,c("1","2","3","4"),TRUE)

Word Cloud

par(mar=c(4.5,5.5,2,1), mfrow=c(1,length(CareerGroup)))
set.seed(123)

Word.Cloud.Group(inaug.list,CareerGroup)

Sentimental Analysis

par(mar=c(4.5,5.5,2,1), mfrow=c(length(CareerGroup),1))
# Create barplot of average value of emotions for each group
Sentimental.Analysis.Plots(sentence.list,CareerGroup)

Below are the emotionally charged sentences of each of these groups.

# What are the emotionally charged sentences?  
Sentimental.Sentence.Searcher(sentence.list,CareerGroup,c("1","2","3","4"))

We performed the k-means clustering of the Lawyer group, as size of other groups are quite small.

par(mar=c(4.5,5.5,2,1), mfrow=c(2,1))
# Lawyer
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,CareerGroup[1],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

Served During War Era?

Overview

Served During War Era? Number of Presidents
No War Era 38
War Era 11

Staging

The following website was used as a reference to understand more about the U.S. history. It presents a timeline of the U.S. hitory, which specifies the wars the U.S. was involded in.

POTUS are assigned into two groups based on whether it was war era while they were serving.

  • 11 POTUS who served during war eras include: James Madison(War of 1812), James Polk(Mexican War), Abraham Lincoln(Civil War), William McKinley(Spanish-American War), Woodrow Wilson(WWI), Franklin D. Roosevelt(WWII), Harry S. Truman(Korean War), Lyndon Johnson(Vietam War), Richard Nixon(Vietnam War), George Bush(Gulf War), George W. Bush(War on Terror).

  • The rest 38 POTUS were serving not during war era.

### Served During War Era?  
# Indentify POTUS who served during war eras 
war.era <- c("JamesMadison", "JamesKPolk", "AbrahamLincoln", 
             "WilliamMcKinley", "WoodrowWilson", "FranklinDRoosevelt",
             "HarrySTruman", "LyndonBJohnson", "RichardNixon",
             "GeorgeBush", "GeorgeWBush")
nowar.era <- inaug.list$File[!(inaug.list$File%in%war.era)] 

# Create group and group name
WarGroup <- list("No War Era"=nowar.era, "War Era"=war.era)
#WarGroup.name <- "Whether Served During War Era" 

summary(WarGroup)[,1]

Length of Speeches

par(mar=c(4.5,3,2,3), mfrow=c(1,2))

# Create bar plot of each speech
Speech.Length.Bar.Plot(inaug.list,WarGroup)
# Create box plot of each speech by group 
Speech.Length.Box.Plot(inaug.list,WarGroup)

Length of Sentences

par(mar=c(4.5,5.5,2,1), mfrow=c(1,2))

Bee.Swarm.Plot(sentence.list,WarGroup)
Bee.Swarm.Plot.Group(sentence.list,WarGroup)

Below listed the shortest and longest sentenses in the speech.

Sentence.Searcher.Group(sentence.list,WarGroup,c("1","2","3","4"),FALSE)
Sentence.Searcher.Group(sentence.list,WarGroup,c("1","2","3","4"),TRUE)

Word Cloud

par(mar=c(4.5,5.5,2,1), mfrow=c(1,length(WarGroup)))
set.seed(123)

Word.Cloud.Group(inaug.list,WarGroup)

Sentimental Analysis

par(mar=c(4.5,5.5,2,1), mfrow=c(length(WarGroup),1))
# Create barplot of average value of emotions for each group
Sentimental.Analysis.Plots(sentence.list,WarGroup)

Below are the emotionally charged sentences of each of these two groups.

# What are the emotionally charged sentences?  
Sentimental.Sentence.Searcher(sentence.list,WarGroup,c("1","2","3","4"))

We performed the k-means clustering for the two groups. The results can be visualized as shown below.

par(mar=c(4.5,5.5,2,1), mfrow=c(2,1))
# No War Era
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,WarGroup[1],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)
# War Era
## Perform k-means clustering 
clustering.rst <- Sentimental.Sentence.KMeans(sentence.list,WarGroup[2],c("1","2","3","4"))
## Visualze clustering results 
fviz_cluster(clustering.rst[[1]], 
             stand=F, repel= TRUE,
             data = clustering.rst[[2]][,-1], 
             xlab="", xaxt="n",
             show.clust.cent=FALSE)

References

LS0tDQp0aXRsZTogIkRhdGEgU3Rvcnkgb24gSW5hdWd1cmFsIFNwZWVjaGVzIG9mIFBPVFVTIg0KYXV0aG9yOiAiWWl5aSBaaGFuZyINCmRhdGU6ICJGZWJydWFyeSAyMDE4Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCiMjIEludHJvZHVjdGlvbiAgDQpXaGF0IGFyZSB0aGUgbW9zdCBtZW1vcmFibGUgaW5hdWd1cmFibGUgYWRkcmVzcyBxdW90ZXMgdGhhdCB5b3UgY2FuIHRoaW5rIG9mIG9mZiB0aGUgdG9wIG9mIHlvdXIgaGVhZD8gTGV0IG1lIHNoYXJlIHR3byBvZiBteSBmYXZvcml0ZXMgYW5kIHNlZSBpZiB0aGV5IGFyZSB5b3VycyB0b28uIA0KDQoiVGhpcyBncmVhdCBuYXRpb24gd2lsbCBlbmR1cmUgYXMgaXQgaGFzIGVuZHVyZWQsIHdpbGwgcmV2aXZlIGFuZCB3aWxsIHByb3NwZXIuIFNvLCBmaXJzdCBvZiBhbGwsIGxldCBtZSBhc3NlcnQgbXkgZmlybSBiZWxpZWYgdGhhdCB0aGUgb25seSB0aGluZyB3ZSBoYXZlIHRvIGZlYXIgaXMgZmVhciBpdHNlbGYuIg0KKi0gRnJhbmtsaW4gRC4gUm9vc2V2ZWx0LCBNYXJjaCA0LCAxOTMzLioNCg0KIk15IGZlbGxvdyBBbWVyaWNhbnM6IEFzayBub3Qgd2hhdCB5b3VyIGNvdW50cnkgY2FuIGRvIGZvciB5b3UgLSBhc2sgd2hhdCB5b3UgY2FuIGRvIGZvciB5b3VyIGNvdW50cnkuIE15IGZlbGxvdyBjaXRpemVucyBvZiB0aGUgd29ybGQ6IEFzayBub3Qgd2hhdCBBbWVyaWNhIHdpbGwgZG8gZm9yIHlvdSwgYnV0IHdoYXQgdG9nZXRoZXIgd2UgY2FuIGRvIGZvciB0aGUgZnJlZWRvbSBvZiBtYW4uIg0KKi0gSm9obiBGLiBLZW5uZWR5LCBKYW4uIDIwLCAxOTYxLioNCg0KQWx0aG91Z2ggdGhlcmUgYXJlIGEgZmV3IHZlcnkgbWVtb3JhYmxlLCBtb3N0IGluYXVndXJhbCBhZGRyZXNzZXMgaGF2ZSBiZWVuIGxvbmcgZm9yZ290dGVuLiBUaGlzIG5vdGUgYm9vayB3aWxsIGdpdmUgdXMgYSBjaGFuY2UgdG8gdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBpbmF1Z3VyYWwgc3BlZWNoZXMgb2YgdGhlIFByZXNpZGVudCBvZiB0aGUgVS5TLiAoUE9UVVMpLCBidXQgZnJvbSBhIGRhdGEgc2NpZW50aWMgcGVyc3BlY3RpdmUuIFRoZSBzY29wZSBvZiB0aGlzIGFuYWx5c2lzIGluY2x1ZGVzIDU4IEluYXVndXJhbCBTcGVlY2hlcyBvZiAzOSBQT1RVUywgd2hpY2ggYXJlIHNjcmFwcGVkIGZyb20gW1RoZSBBbWVyaWNhbiBQcmVzaWRlbnRjeSBQcm9qZWN0XVsxXS4gDQoNCg0KIyMgU3RhZ2luZyB7LnRhYnNldH0NCiMjIyBPdmVydmlldyAgDQoNCiogICAqKlN0ZXAgMCoqIC0gSW5zdGFsbCBQYWNrYWdlcyBhbmQgTG9hZCBMaWJyYXJpZXMgIA0KKiAgICoqU3RlcCAxKiogLSBEYXRhIEhhcnZlc3Q6IFNjcmFwIFNwZWVjaCBVUkxzIGFuZCBSZWFkIGluIFNwZWVjaGVzICANCiogICAqKlN0ZXAgMioqIC0gRGF0YSBQcm9jZXNzaW5nICANCiogICAqKlN0ZXAgMyoqIC0gQnVpbGQgRnVuY3Rpb25zICANCg0KIyMjIFN0ZXAgMCAtIEluc3RhbGwgYW5kIExvYWQgTGlicmFyaWVzICANCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIEluc3RhbGwgYW5kIExvYWQgTGlicmFyaWVzICANCiMgUGFja2FnZXMgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIHRoaXMgbm90ZWJvb2sgDQpwYWNrYWdlcy51c2VkPWMoInJ2ZXN0IiwgInhsc3giLCAidGliYmxlIiwgDQogICAgICAgICAgICAgICAgInRtIiwgInRpZHl0ZXh0IiwgImRwbHlyIiwNCiAgICAgICAgICAgICAgICAid29yZGNsb3VkIiwgInFkYXAiLCAic3l1emhldCIsDQogICAgICAgICAgICAgICAgImJlZXN3YXJtIiwgIlJDb2xvckJyZXdlciIsICJzZW50aW1lbnRyIiwgIA0KICAgICAgICAgICAgICAgICAiZ3Bsb3RzIiwgImZhY3RvZXh0cmEiLCAiTUFTUyIsIA0KICAgICAgICAgICAgICAgICAic2NhbGVzIiwgIlJBTk4iLCAidG9waWNtb2RlbHMiKQ0KDQojIENoZWNrIHBhY2thZ2VzIHRoYXQgbmVlZCB0byBiZSBpbnN0YWxsZWQNCnBhY2thZ2VzLm5lZWRlZD1zZXRkaWZmKHBhY2thZ2VzLnVzZWQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJzZWN0KGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2FnZXMudXNlZCkpDQojIEluc3RhbGwgYWRkaXRpb25hbCBwYWNrYWdlcw0KaWYobGVuZ3RoKHBhY2thZ2VzLm5lZWRlZCk+MCl7DQogIGluc3RhbGwucGFja2FnZXMocGFja2FnZXMubmVlZGVkLCBkZXBlbmRlbmNpZXMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgIHJlcG9zPSdodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnJykNCn0NCg0KIyBMb2FkIGxpYnJhcmllcyAgDQpsaWJyYXJ5KCJydmVzdCIpDQpsaWJyYXJ5KCJ4bHN4IikNCmxpYnJhcnkoInRpYmJsZSIpDQpsaWJyYXJ5KCJ0bSIpDQpsaWJyYXJ5KCJ0aWR5dGV4dCIpDQpsaWJyYXJ5KCJNQVNTIikNCmxpYnJhcnkoImRwbHlyIikNCmxpYnJhcnkoIndvcmRjbG91ZCIpDQojIFlvdSBtYXkgbmVlZCB0byBydW4NCiMgc3VkbyBsbiAtZiAtcyAkKC91c3IvbGliZXhlYy9qYXZhX2hvbWUpL2pyZS9saWIvc2VydmVyL2xpYmp2bS5keWxpYiAvdXNyL2xvY2FsL2xpYg0KIyBpbiBvcmRlciB0byBsb2FkIHFkYXANCiNsaWJyYXJ5KHJKYXZhKQ0KbGlicmFyeSgicWRhcCIpDQpsaWJyYXJ5KCJzeXV6aGV0IikNCmxpYnJhcnkoImJlZXN3YXJtIikNCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpDQpsaWJyYXJ5KCJncGxvdHMiKQ0KbGlicmFyeSgic2NhbGVzIikNCiNsaWJyYXJ5KCJzZW50aW1lbnRyIikNCmxpYnJhcnkoImZhY3RvZXh0cmEiKQ0KI2xpYnJhcnkoIlJBTk4iKQ0KbGlicmFyeSgidG9waWNtb2RlbHMiKQ0KDQojIE9idGFpbiBzcGVlY2hGdW5jcyBmdW5jdGlvbnMgZnJvbSB0aGUgd2Vic2l0ZSBsaXN0ZWQgYmVsb3cgDQojICdzcGVlY2hGdW5jcycgaW5jbHVkZXMgJ2Yuc3BlZWNobGlua3MnLCAnZi5wbG90c2VudC5sZW4nLCBhbmQgJ2Yuc21vb3RoLnRvcGljJw0KIyBodHRwczovL2dpdGh1Yi5jb20vVFpzdGF0c0FEUy9BRFNfVGVhY2hpbmcvYmxvYi9tYXN0ZXIvVHV0b3JpYWxzL3drMi1UZXh0TWluaW5nL2xpYi9zcGVlY2hGdW5jcy5SDQpzb3VyY2UoIi4uL2xpYi9zcGVlY2hGdW5jcy5SIikNCiMgT2J0YWluIHBsb3RzdGFja2VkIGZ1bmN0aW9ucyBmcm9tIHRoZSB3ZWJzaXRlIGxpc3RlZCBiZWxvdyAgDQojICdwbG90c3RhY2tlZCcgaW5jbHVkZXMgJ3Bsb3Quc3RhY2tlZCcNCiMgaHR0cHM6Ly9naXRodWIuY29tL1Rac3RhdHNBRFMvQURTX1RlYWNoaW5nL2Jsb2IvbWFzdGVyL1R1dG9yaWFscy93azItVGV4dE1pbmluZy9saWIvcGxvdHN0YWNrZWQuUg0KI3NvdXJjZSgiLi4vbGliL3Bsb3RzdGFja2VkLlIiKQ0KYGBgDQoNCipUaGlzIG5vdGVib29rIHdhcyBwcmVwYXJlZCB3aXRoIHRoZSBmb2xsb3dpbmcgZW52aXJvbm1lbnRhbCBzZXR0aW5ncy4qICANCmBgYHtyfQ0KcHJpbnQoUi52ZXJzaW9uKQ0KYGBgDQoqQ3VycmVudCB3b3JraW5nIGRpcmVjdG9yIGlzKiANCmBgYHtyfQ0KZ2V0d2QoKQ0KYGBgDQoNCiMjIyBTdGVwIDEgLSBEYXRhIEhhcnZlc3QgIA0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyMgRGF0YSBIYXJ2ZXN0OiBTY3JhcCBVUkxzIG9mIEluYXVndXJhbCBTcGVlY2hlcyBvZiBQT1RVUyBhbmQgUmVhZCBpbiBTcGVlY2hlcyANCiMgR2V0IHNwZWVjaGVzIFVSTHMNCm1haW4ucGFnZSA8LSByZWFkX2h0bWwoeCA9ICJodHRwOi8vd3d3LnByZXNpZGVuY3kudWNzYi5lZHUvaW5hdWd1cmFscy5waHAiKQ0KIyBmLnNwZWVjaGxpbmtzIGlzIGEgZnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgbGlua3MgZnJvbSB0aGUgbGlzdCBvZiBzcGVlY2hlcy4gDQppbmF1Zy51cmxzIDwtIGYuc3BlZWNobGlua3MobWFpbi5wYWdlKSANCiMgUmVtb3ZlIHRoZSBsYXN0IGxpbmUsIGlycmVsZXZhbnQgZHVlIHRvIGVycm9yLiAgDQppbmF1Zy51cmxzIDwtIGluYXVnLnVybHNbLW5yb3coaW5hdWcudXJscyksXSANCiMgRm9ybWF0IERhdGUNCmluYXVnLnVybHNbLDFdIDwtIGFzLkRhdGUoaW5hdWcudXJsc1ssMV0sIGZvcm1hdD0iJUIgJWUsICVZIikNCg0KIyBSZWFkIGluZm9ybWF0aW9uIGZpbGUgcHJvdmlkZWQgIi4uL2RhdGEvSW5hdWd1cmF0aW9uSW5mby54bHN4Ig0KZmlsZS5wYXRoIDwtICIuLi9kYXRhL0luYXVndXJhdGlvbkluZm8ueGxzeCINCmluYXVnLmluZm8gPC0gcmVhZC54bHN4KGZpbGUucGF0aCwgc2hlZXRJbmRleCA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KIyBBZGQgIkRhdGUiIGFuZCAiVVJMcyIgY29sdW1ucyB0byBpbmF1Zy5pbmZvLCBuYW1lZCBhcyBpbmF1Zy5saXN0DQppbmF1Zy5saXN0IDwtIGFkZF9jb2x1bW4oaW5hdWcuaW5mbywgRGF0ZSA9IGluYXVnLnVybHNbLDFdLCAuYWZ0ZXIgPSA0KQ0KaW5hdWcubGlzdCA8LSBhZGRfY29sdW1uKGluYXVnLmxpc3QsIFVSTHMgPSBpbmF1Zy51cmxzWywyXSkNCiMgVXBkYXRlIHRoZSAiV29yZHMiIHZhcmlhYmxlIGZvciBUcnVtcCdzIHNwZWVjaA0KaW5hdWcubGlzdCRXb3Jkc1s1OF0gPC0gMTQzMyANCiMgTWFrZSAiV29yZHMiIGFzIG51bWVyaWMgdmFyaWFibGUNCmluYXVnLmxpc3QkV29yZHMgPC0gYXMubnVtZXJpYyhpbmF1Zy5saXN0JFdvcmRzKSANCiMgTWFrZSBuYW1lIGNvbnNpc3RlbnQNCmluYXVnLmxpc3QkUHJlc2lkZW50WzI1XSA8LSAiR3JvdmVyIENsZXZlbGFuZCIgDQppbmF1Zy5saXN0JFByZXNpZGVudFsyN10gPC0gIkdyb3ZlciBDbGV2ZWxhbmQiIA0KaW5hdWcubGlzdCRGaWxlWzI1XSA8LSAiR3JvdmVyQ2xldmVsYW5kIg0KaW5hdWcubGlzdCRGaWxlWzI3XSA8LSAiR3JvdmVyQ2xldmVsYW5kIg0KDQojIFJlYWQgaW4gZWFjaCBzcGVlY2ggIA0KIyBBZGQgYW5kIHN0YWdlIGEgJ0Z1bGx0ZXh0JyBjb2x1bW4gdG8gIGluYXVnLmxpc3QNCmluYXVnLmxpc3QkRnVsbHRleHQgPC0gTkEgDQojIEluZGVudGlmeSB0aGUgZm9sZGVyICIuLi9kYXRhL0luYXVndXJhbFNwZWVjaGVzLyIgDQojIHdoZXJlIHRvIHNhdmUgZWFjaCBzcGVlY2ggYXMgIGluZGl2aWR1YWwgJy50eHQnIGZpbGUgDQpmb2xkZXIucGF0aCA8LSAiLi4vZGF0YS9JbmF1Z3VyYWxTcGVlY2hlcy8iDQojIExvb3Agb3ZlciBlYWNoIHJvdyBpbiBpbmF1Zy5saXN0IA0KZm9yKGkgaW4gc2VxKG5yb3coaW5hdWcubGlzdCkpKSB7DQogICMgUmVhZCBzcGVlY2ggZnJvbSB0aGUgVVJMIGFuZCBTdG9yZSBpdCBpbiB2YXJpYWJsZSAndGV4dCcgDQogIHRleHQgPC0gcmVhZF9odG1sKGluYXVnLmxpc3QkVVJMc1tpXSkgJT4lICMgTG9hZCB0aGUgcGFnZQ0KICAgIGh0bWxfbm9kZXMoIi5kaXNwbGF5dGV4dCIpICU+JSAjIElzbG9hdGUgdGhlIHRleHQNCiAgICBodG1sX3RleHQoKSAjIEdldCB0aGUgdGV4dCANCiAgIyBVcGRhdGUgdGhlICdGdWxsdGV4dCcgY29sdW1uIG9mIGluYXVnLmxpc3Qgd2l0aCBzcGVlY2ggdGV4dCANCiAgaW5hdWcubGlzdCRGdWxsdGV4dFtpXSA8LSB0ZXh0IA0KICAjIENyZWF0ZSBmaWxlIG5hbWUgb2YgZWFjaCBpbmRpdmlkdWFsIHNwZWVjaA0KICBmaWxlbmFtZSA8LSBwYXN0ZTAoZm9sZGVyLnBhdGgsIA0KICAgICAgICAgICAgICAgICAgICAgImluYXVnIiwNCiAgICAgICAgICAgICAgICAgICAgIGluYXVnLmxpc3QkRmlsZVtpXSwgIi0iLCANCiAgICAgICAgICAgICAgICAgICAgIGluYXVnLmxpc3QkVGVybVtpXSwgIi50eHQiKSANCiAgIyBSZWFkIHRleHQgaW50byB0aGUgJy50eHQnIGZpbGUgY3JlYXRlZA0KICBzaW5rKGZpbGUgPSBmaWxlbmFtZSkgJT4lICMgT3BlbiBmaWxlIHRvIHdyaXRlIA0KICBjYXQodGV4dCkgICMgV3JpdGUgdGhlIGZpbGUNCiAgc2luaygpICMgQ2xvc2UgdGhlIGZpbGUNCn0NCg0KIyBTYXZlIHRoZSBpbmF1Zy5saXN0IGFzICcuY3N2JyBmaWxlIHVuZGVyIGZvbGRlciAiLi4vb3V0cHV0LyINCndyaXRlLmNzdihpbmF1Zy5saXN0LCBmaWxlID0gIi4uL291dHB1dC9pbmF1Z19saXN0LmNzdiIpDQojc3VtbWFyeShpbmF1Zy5saXN0KQ0KYGBgDQpUaGUgc2NvcGUgb2YgdGhpcyBhbmFseXNpcyBpbmNsdWRlcyBgciBucm93KGluYXVnLmxpc3QpYCBJbmF1Z3VyYWwgU3BlZWNoZXMgb2YgYHIgbGVuZ3RoKHVuaXF1ZShpbmF1Zy5saXN0JEZpbGUpKWAgUE9UVVMsIHdobyBhcmUgbGlzdGVkIGJlbG93LiAgDQpgYGB7cn0NCnVuaXF1ZShpbmF1Zy5saXN0JFByZXNpZGVudCkNCmFsbC5GaWxlIDwtIHVuaXF1ZShpbmF1Zy5saXN0JEZpbGUpDQoNCmBgYA0KDQojIyMgU3RlcCAyIC0gRGF0YSBQcm9jZXNzaW5nICANCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIERhdGEgUHJvY2Vzc2luZyAgDQojIFN0YWdlIGEgZGF0YS5mcmFtZSAnc2VudGVuY2UubGlzdCcNCnNlbnRlbmNlLmxpc3QgPC0gTlVMTA0KIyBMb29wIG92ZXIgZWFjaCByb3cgaW4gaW5hdWcubGlzdCANCmZvcihpIGluIDE6bnJvdyhpbmF1Zy5saXN0KSl7DQogICMgU3BsaXQgZWFjaCBzcGVlY2ggaW50byBpbmRpdmlkdWFsIHNlbnRlbmNlcyANCiAgc2VudGVuY2VzIDwtIHNlbnRfZGV0ZWN0KGluYXVnLmxpc3QkRnVsbHRleHRbaV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kbWFya3MgPSBjKCI/IiwgIi4iLCAiISIsICJ8IiwiOyIpKQ0KICANCiAgaWYobGVuZ3RoKHNlbnRlbmNlcyk+MCl7DQogICAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIHdvcmRzIGluIGVhY2ggc2VudGVuY2UNCiAgICB3b3JkLmNvdW50IDwtIHdvcmRfY291bnQoc2VudGVuY2VzKQ0KICAgICMgQ2FsY3VsYXRlIHRoZSBwcmVzZW5jZSBvZiBlaWdodCBkaWZmZXJlbnQgZW1vdGlvbnMgaW4gZWFjaCBzZW50ZW5jZQ0KICAgIGVtb3Rpb25zIDwtIGdldF9ucmNfc2VudGltZW50KHNlbnRlbmNlcykNCiAgICAjY29sbmFtZXMoZW1vdGlvbnMpPXBhc3RlMCgiZW1vLiIsIGNvbG5hbWVzKGVtb3Rpb25zKSkNCiAgICAjIFNjYWxlIGVtb3Rpb25zIGJ5IHdvcmQuY291bnQNCiAgICAjIEluIGNhc2UgdGhlIHdvcmQgY291bnRzIGFyZSB6ZXJvcywgYWRkIDAuMDEgdG8gd29yZC5jb3VudCANCiAgICBlbW90aW9ucyA8LSBkaWFnKDEvKHdvcmQuY291bnQrMC4wMSkpJSolYXMubWF0cml4KGVtb3Rpb25zKQ0KICAgICMgVXBkYXRlIHNlbnRlbmNlLmxpc3QgDQogICAgIyBDb2x1bW5zIGluY2x1ZGUgY29sdW1ucyBvZiBpbmF1Zy5saXN0LCBzZW50ZWNlcywgd29yZC5jb3VudCwgZW1vdGlvbnMoMTApLCBzZW50LmlkDQogICAgIyBSb3dzIGFkZGVkIHdoaWxlIGxvb3Bpbmcgb3ZlciBlYWNoIHJvdyBpbiBpbmF1Zy5saXN0IA0KICAgIHNlbnRlbmNlLmxpc3QgPC0gcmJpbmQoc2VudGVuY2UubGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICBjYmluZCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyBDb2x1bW5zIG9mIGluYXVnLmxpc3QNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW5hdWcubGlzdFtpLC1uY29sKGluYXVnLmxpc3QpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyBPbmUgY29sdW1uIHZlY3RvciBvZiBzZW50ZW5jZXMgaW4gaW5kaXZpZHVhbCBzcGVlY2ggDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnRlbmNlcz1hcy5jaGFyYWN0ZXIoc2VudGVuY2VzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICMgT25lIGNvbHVtbiBvZiB3b3JkIGNvdW50IG9mIGNvcnJlc3BvbmRpbmcgc2VudGVuY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgd29yZC5jb3VudCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyA4IGVtb3Rpb25zIGNvbHVtbnMgKyAyIHZhbGVuY2UgY29sdW1ucyAocG9zaXRpdmUvbmVnYXRpdmUpDQogICAgICAgICAgICAgICAgICAgICAgICAgIGVtb3Rpb25zLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAjIEFzc2lnbiBjb25zZWN1dGl2ZSBpZCB0byBlYWNoIHNlbnRlbmNlDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnQuaWQ9MTpsZW5ndGgoc2VudGVuY2VzKSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICkNCiAgfQ0KfQ0KIyBDbGVhbiB1cCBzZW50ZW5jZS5saXN0DQojIFNvbWUgbm9uLXNlbnRlbmNlcyBleGlzdCBpbiByYXcgZGF0YSBkdWUgdG8gZXJyb25lb3VzIGV4dHJhIGVuZC1vZi1zZW50ZW5jZSBtYXJrcy4NCnNlbnRlbmNlLmxpc3QgPC0gc2VudGVuY2UubGlzdCU+JWZpbHRlcighaXMubmEod29yZC5jb3VudCkpIA0KYGBgDQoNCiMjIyBTdGVwIDMgLSBCdWlsZCBGdW5jdGlvbnMgey50YWJzZXR9ICANCiMjIyMgQXNzaWduIEdyb3VwIGFuZCBDb2xvcg0KYGBge3J9DQojIGFzc2lnbi5jb2xvcg0KIyBBc3NpZ24gSW4uRmlsZSBhIGNvbG9yIGNvcnJlc3BvbmQgdG8gdGhlIGdyb3VwcyBpbiBJbi5Hcm91cA0KYXNzaWduLmNvbG9yIDwtIGZ1bmN0aW9uKEluLnZhciwgSW4uR3JvdXAudmFyLA0KICAgICAgICAgICAgICAgICAgICAgICAgIEluLlBhbGV0dGUudmFyPSJTZXQxIiwgSW4uYWxwaGEudmFyPTEpew0KICAjIEluLkdyb3VwLnZhciBpcyBhIGxpc3Qgb2YgbmFtZWQgbGlzdHMuICAgDQogIGNvbC51c2UgPC0gYWxwaGEoYnJld2VyLnBhbChsZW5ndGgoSW4uR3JvdXAudmFyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLlBhbGV0dGUudmFyKSwNCiAgICAgICAgICAgICAgICAgICBJbi5hbHBoYS52YXIpDQogICAgZm9yIChpIGluIDE6bGVuZ3RoKEluLkdyb3VwLnZhcikpew0KICAgICAgaWYgKEluLnZhciAlaW4lIHVubGlzdChJbi5Hcm91cC52YXJbaV0pKSB7cmV0dXJuKGNvbC51c2VbaV0pfQ0KICAgIH0NCn0NCg0KIyBhc3NpZ24uZ3JvdXANCiMgQXNzaWduIEluLnZhciB0byBncm91cHMgaW4gSW4uR3JvdXAudmFyDQphc3NpZ24uZ3JvdXAgPC0gZnVuY3Rpb24oSW4udmFyLCBJbi5Hcm91cC52YXIpew0KICAjIEluLkdyb3VwLnZhciBpcyBhIGxpc3Qgb2YgbmFtZWQgbGlzdHMuIA0KICBmb3IgKGkgaW4gMTpsZW5ndGgoSW4uR3JvdXAudmFyKSl7DQogICAgaWYgKEluLnZhciAlaW4lIHVubGlzdChJbi5Hcm91cC52YXJbaV0pKSB7cmV0dXJuKG5hbWVzKEluLkdyb3VwLnZhcilbaV0pfQ0KICAgIH0NCn0NCg0KDQpgYGANCiMjIyMgTGVuZ3RoIG9mIFNwZWVjaGVzDQpgYGB7cn0NCiMgU3BlZWNoLkxlbmd0aC5CYXIuUGxvdA0KIyBDcmVhdGUgQmFyIFBsb3QgZm9yIGV2ZXJ5IEZpbGUgaW4gZ3JvdXBzIG9mIEluLkdyb3VwDQojIEluLkdyb3VwIGlzIGEgbGlzdCBvZiBuYW1lZCBsaXN0cyANCiMgSW4uVGVybSBpcyBhIGxpc3Qgb2YgbmFtZWQgbGlzdHMNClNwZWVjaC5MZW5ndGguQmFyLlBsb3QgPC0gZnVuY3Rpb24oSW4ubGlzdD1pbmF1Zy5saXN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5Hcm91cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLlRlcm09bGlzdCgiRmlyc3QgVGVybSI9IjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNlY29uZCBUZXJtIj0iMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGhpcmQgVGVybSI9IjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZvdXJ0aCBUZXJtIj0iNCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5ieS5UZXJtPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5QYWxldHRlPSJTZXQxIiwgSW4uYWxwaGE9MSl7DQogIHZhciA8LSBJbi5saXN0JT4lDQogICAgICAgICAgZmlsdGVyKEZpbGUlaW4ldW5saXN0KEluLkdyb3VwKSwNCiAgICAgICAgICAgICAgICAgVGVybSVpbiV1bmxpc3QoSW4uVGVybSkpDQogIHZhciRGaWxlIDwtIGZhY3Rvcih2YXIkRmlsZSkNCiMgIHZhciRGaWxlT3JkZXJlZCA8LSByZW9yZGVyKHZhciRGaWxlLCB2YXIkV29yZHMsIG1lYW4sIG9yZGVyPVQpDQogIGlmIChJbi5ieS5UZXJtPT1GQUxTRSkgew0KICAgIHZhciRjb2wuaWQgPC0gdW5saXN0KGxhcHBseSh2YXIkRmlsZSwgYXNzaWduLmNvbG9yLCBJbi5Hcm91cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5QYWxldHRlLCBJbi5hbHBoYSkpDQogICAgbGVnZW5kLmlkIDwtIG5hbWVzKEluLkdyb3VwKQ0KICB9ZWxzZXsNCiAgICB2YXIkY29sLmlkIDwtIHVubGlzdChsYXBwbHkodmFyJFRlcm0sIGFzc2lnbi5jb2xvciwgSW4uVGVybSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5QYWxldHRlLCBJbi5hbHBoYSkpDQogICAgbGVnZW5kLmlkIDwtIG5hbWVzKEluLlRlcm0pDQogIH0NCiAgDQogIHggPC0gYmFycGxvdCh2YXIkV29yZHMsIHNwYWNlID0gMSwNCiAgICAgICAgICAgICAgIGNvbD12YXIkY29sLmlkLA0KICAgICAgICAgICAgICAgY2V4LmF4aXMgPSAwLjcsDQogICAgICAgICAgICAgICBtYWluID0gIk51bWJlciBvZiBXb3JkcyBpbiBhIFNwZWVjaCIgDQogICAgICAgICAgICAgICkNCiAgdGV4dChjZXg9MC41LCB4PXgtMC4yNSwgeT0tMS4yNSwgDQogICAgICAgYWRqPTEsIHNydCA9IDYwLCB4cGQgPSBUUlVFLA0KICAgICAgIGxhYmVscyA9IHBhc3RlKHN1YnN0cih2YXIkRGF0ZSwxLDQpLA0KICAgICAgICAgICAgICAgICAgICAgIHZhciRQcmVzaWRlbnQpKQ0KICBsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQ9IDAuMDIsIGxlZ2VuZCA9bGVnZW5kLmlkICwgDQogICAgICAgICB0ZXh0LmNvbCA9IGJyZXdlci5wYWwobGVuZ3RoKGxlZ2VuZC5pZCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uUGFsZXR0ZSlbMTpsZW5ndGgobGVnZW5kLmlkKV0sIA0KICAgICAgICAgY2V4ID0gMC43LCBib3gubHR5PTIgKQ0KfQ0KDQojIFNwZWVjaC5MZW5ndGguQm94LlBsb3QNCiMgQ3JlYXRlIEJveCBQbG90IGZvciBncm91cHMgaW4gSW4uR3JvdXANCiMgSW4uR3JvdXAgaXMgYSBsaXN0IG9mIG5hbWVkIGxpc3RzIA0KIyBJbi5UZXJtIGlzIGEgbGlzdCBvZiBuYW1lZCBsaXN0cw0KU3BlZWNoLkxlbmd0aC5Cb3guUGxvdCA8LSBmdW5jdGlvbihJbi5saXN0PWluYXVnLmxpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5Hcm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uVGVybT1saXN0KCJGaXJzdCBUZXJtIj0iMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2Vjb25kIFRlcm0iPSIyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUaGlyZCBUZXJtIj0iMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRm91cnRoIFRlcm0iPSI0IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLmJ5LlRlcm09RkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLlBhbGV0dGU9IlNldDEiLCBJbi5hbHBoYT0xKXsNCiAgdmFyIDwtIEluLmxpc3QlPiUNCiAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoSW4uR3JvdXApLA0KICAgICAgICAgICAgICAgICBUZXJtJWluJXVubGlzdChJbi5UZXJtKSkNCiAgdmFyJEZpbGUgPC0gZmFjdG9yKHZhciRGaWxlKQ0KIyAgdmFyJEZpbGVPcmRlcmVkIDwtIHJlb3JkZXIodmFyJEZpbGUsIHZhciRXb3JkcywgbWVhbiwgb3JkZXI9VCkNCiAgaWYgKEluLmJ5LlRlcm09PUZBTFNFKSB7DQogICAgdmFyJGdyb3VwLnZhciA8LSB1bmxpc3QobGFwcGx5KHZhciRGaWxlLCBhc3NpZ24uZ3JvdXAsIEluLkdyb3VwKSkgIA0KICAgIHZhciRncm91cC52YXIgPC0gZmFjdG9yKHZhciRncm91cC52YXIsIG5hbWVzKEluLkdyb3VwKSkNCiAgICBsZWdlbmQuaWQgPC0gbmFtZXMoSW4uR3JvdXApDQogIH1lbHNlew0KICAgIHZhciRncm91cC52YXIgPC0gdW5saXN0KGxhcHBseSh2YXIkVGVybSwgYXNzaWduLmdyb3VwLCBJbi5UZXJtKSkgDQogICAgdmFyJGdyb3VwLnZhciA8LSBmYWN0b3IodmFyJGdyb3VwLnZhciwgbmFtZXMoSW4uVGVybSkpDQogICAgbGVnZW5kLmlkIDwtIG5hbWVzKEluLlRlcm0pDQogIH0NCg0KICBib3hwbG90KHZhciRXb3Jkc352YXIkZ3JvdXAudmFyLA0KICAgICAgICAgIGNvbD1hbHBoYShicmV3ZXIucGFsKGxlbmd0aChsZWdlbmQuaWQpLEluLlBhbGV0dGUpLA0KICAgICAgICAgICAgICAgICAgICBJbi5hbHBoYSlbMTpsZW5ndGgobGVnZW5kLmlkKV0sDQogICAgICAgICAgY2V4LmF4aXMgPSAwLjcsDQogICAgICAgICAgbWFpbiA9ICJOdW1iZXIgb2YgV29yZHMgaW4gYSBTcGVlY2giICkNCn0NCmBgYA0KIyMjIyBMZW5ndGggb2YgU2VudGVuY2VzDQpgYGB7cn0NCiMgQmVlLlN3YXJtLlBsb3QNCiMgQ3JlYXRlIEJlZSBTd2FybSBQbG90IGZvciBldmVyeSBGaWxlIGluIGdyb3VwcyBvZiBJbi5Hcm91cA0KQmVlLlN3YXJtLlBsb3QgPC0gZnVuY3Rpb24oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCBJbi5Hcm91cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBJbi5QYWxldHRlPSJTZXQxIiwgSW4uYWxwaGE9MC40KXsNCiAgdmFyIDwtIEluLmxpc3QlPiUNCiAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoSW4uR3JvdXApKQ0KICB2YXIkRmlsZSA8LSBmYWN0b3IodmFyJEZpbGUpDQogIHZhciRGaWxlT3JkZXJlZCA8LSByZW9yZGVyKHZhciRGaWxlLCB2YXIkd29yZC5jb3VudCwgbWVhbiwgb3JkZXI9VCkNCiAgDQogIGZvciAoaSBpbiAxOmxlbmd0aChJbi5Hcm91cCkpew0KICAgIGlmIChpPT0xKXsNCiAgICAgIGJlZXN3YXJtKHdvcmQuY291bnR+RmlsZU9yZGVyZWQsIA0KICAgICAgICAgICAgIGRhdGEgPSB2YXIlPiUNCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEZpbGUlaW4ldW5saXN0KEluLkdyb3VwW2ldKSksDQogICAgICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsDQogICAgICAgICAgICAgcGNoPTE2LCBjb2w9YWxwaGEoYnJld2VyLnBhbChsZW5ndGgoSW4uR3JvdXApLCBJbi5QYWxldHRlKVtpXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uYWxwaGEpLCANCiAgICAgICAgICAgICBjZXg9MC41NSwgY2V4LmF4aXM9MC41NSwgY2V4LmxhYj0xLA0KICAgICAgICAgICAgIHNwYWNpbmc9NS9ubGV2ZWxzKHZhciRGaWxlT3JkZXJlZCksDQogICAgICAgICAgICAgbGFzPTIsIHlsYWI9IiIsIHhsYWI9IiIsDQogICAgICAgICAgICAgbWFpbj0iTnVtYmVyIG9mIFdvcmRzIGluIGEgU2VudGVuY2UiKQ0KICAgIH0gZWxzZSB7DQogICAgICBiZWVzd2FybSh3b3JkLmNvdW50fkZpbGVPcmRlcmVkLCANCiAgICAgICAgICAgICBkYXRhID0gdmFyJT4lDQogICAgICAgICAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChJbi5Hcm91cFtpXSkpLA0KICAgICAgICAgICAgIGFkZD1UUlVFLA0KICAgICAgICAgICAgIGhvcml6b250YWwgPSBUUlVFLA0KICAgICAgICAgICAgIHBjaD0xNiwgY29sPWFscGhhKGJyZXdlci5wYWwobGVuZ3RoKEluLkdyb3VwKSwgSW4uUGFsZXR0ZSlbaV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLmFscGhhKSwgDQogICAgICAgICAgICAgY2V4PTAuNTUsIGNleC5heGlzPTAuNTUsIGNleC5sYWI9MSwNCiAgICAgICAgICAgICBzcGFjaW5nPTUvbmxldmVscyh2YXIkRmlsZU9yZGVyZWQpDQogICAgICAgICAgICAgKQ0KICAgIH0NCiAgfQ0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgaW5zZXQ9IDAuMDIsIGxlZ2VuZCA9bmFtZXMoSW4uR3JvdXApICwgDQogICAgICAgICB0ZXh0LmNvbCA9IGJyZXdlci5wYWwobGVuZ3RoKEluLkdyb3VwKSxJbi5QYWxldHRlKVsxOmxlbmd0aChJbi5Hcm91cCldLCANCiAgICAgICAgIGNleCA9IDAuNywgYm94Lmx0eT0yICkNCn0NCg0KDQojIEJlZS5Td2FybS5QbG90Lkdyb3VwDQojIENyZWF0ZSBCZWUgU3dhcm0gUGxvdCBmb3IgZ3JvdXBzIGluIEluLkdyb3VwDQpCZWUuU3dhcm0uUGxvdC5Hcm91cCA8LSBmdW5jdGlvbihJbi5saXN0PXNlbnRlbmNlLmxpc3QsIEluLkdyb3VwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLlBhbGV0dGU9IlNldDEiLCBJbi5hbHBoYT0wLjQpew0KICB2YXIgPC0gSW4ubGlzdCU+JQ0KICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChJbi5Hcm91cCkpDQogIHZhciRGaWxlIDwtIGZhY3Rvcih2YXIkRmlsZSkNCiAgdmFyJEZpbGVPcmRlcmVkIDwtIHJlb3JkZXIodmFyJEZpbGUsIHZhciR3b3JkLmNvdW50LCBtZWFuLCBvcmRlcj1UKQ0KICB2YXIkZ3JvdXAudmFyIDwtIHVubGlzdChsYXBwbHkodmFyJEZpbGUsIGFzc2lnbi5ncm91cCwgSW4uR3JvdXApKQ0KICB2YXIkZ3JvdXAudmFyIDwtIGZhY3Rvcih2YXIkZ3JvdXAudmFyLCBuYW1lcyhJbi5Hcm91cCkpDQogICAgDQogIGJlZXN3YXJtKHdvcmQuY291bnR+Z3JvdXAudmFyLCANCiAgICAgICAgICAgICBkYXRhID0gdmFyJT4lZmlsdGVyKEZpbGUlaW4ldW5saXN0KEluLkdyb3VwKSksDQogICAgICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsDQogICAgICAgICAgICAgcGNoPTE2LCBjb2w9YWxwaGEoYnJld2VyLnBhbChsZW5ndGgoSW4uR3JvdXApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uUGFsZXR0ZSlbMTpsZW5ndGgoSW4uR3JvdXApXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uYWxwaGEpLCANCiAgICAgICAgICAgICBjZXg9MC41NSwgY2V4LmF4aXM9MC41NSwgY2V4LmxhYj0xLA0KICAgICAgICAgICAgIHNwYWNpbmc9NS9ubGV2ZWxzKHZhciRGaWxlT3JkZXJlZCksDQogICAgICAgICAgICAgbGFzPTIsIHlsYWI9IiIsIHhsYWI9IiIsDQogICAgICAgICAgICAgbWFpbj0iTnVtYmVyIG9mIFdvcmRzIGluIGEgU2VudGVuY2UiKQ0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgaW5zZXQ9MC4wMiwgbGVnZW5kID1uYW1lcyhJbi5Hcm91cCkgLCANCiAgICAgICAgIHRleHQuY29sID0gYnJld2VyLnBhbChsZW5ndGgoSW4uR3JvdXApLEluLlBhbGV0dGUpWzE6bGVuZ3RoKEluLkdyb3VwKV0sIA0KICAgICAgICAgY2V4ID0gMC43LCBib3gubHR5PTIgKQ0KICBib3hwbG90KHZhciR3b3JkLmNvdW50fnZhciRncm91cC52YXIsIGhvcml6b250YWw9VFJVRSwgDQogICAgICAgICAgY29sPSIjMDAwMGZmMjIiLCBheGVzPUZBTFNFLCBhZGQ9VFJVRSkNCn0NCmBgYA0KDQpgYGB7cn0NCiMgU2VudGVuY2UuU2VhcmNoZXIuR3JvdXANCiMgR2VuZW5yYXRlcyBzaG9ydGVzdCBvciBsb25nZXN0IHNlbnRlbmNlcyBvZiBncm91cHMgaW4gSW4uR3JvdXANCiMgR2VuZXJhdGUgbG9uZ2VzdCBzZW50ZW5jZXMgaWYgSW4uTG9uZz1UcnVlIA0KU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAgPC0gZnVuY3Rpb24oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLkdyb3VwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLlRlcm09YygiMSIsIjIiLCIzIiwiNCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uTG9uZz1UUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLm1pbi53b3Jkcz0zLCBJbi5uLnNlbnRlbmNlcz01KXsNCiAgZGYgPC0gYyhzZXEoMTpJbi5uLnNlbnRlbmNlcykpDQogIGZvciAoaSBpbiAxOmxlbmd0aChJbi5Hcm91cCkpew0KICAgIHZhciA8LSAgIEluLmxpc3QlPiUNCiAgICAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoSW4uR3JvdXBbaV0pLCANCiAgICAgICAgICAgICAgICAgICAgVGVybSVpbiVJbi5UZXJtLA0KICAgICAgICAgICAgICAgICAgICB3b3JkLmNvdW50Pj1Jbi5taW4ud29yZHMpJT4lDQogICAgICAgICAgICAgICBhcnJhbmdlKGRlc2Mod29yZC5jb3VudCkpJT4lDQogICAgICAgICAgICAgICBzZWxlY3Qoc2VudGVuY2VzKQ0KICAgICAgaWYgKEluLkxvbmc9PVRSVUUpew0KICAgICAgICB2YXIgPC0gaGVhZCh2YXIsSW4ubi5zZW50ZW5jZXMpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB2YXIgPC0gdGFpbCh2YXIsSW4ubi5zZW50ZW5jZXMpDQogICAgICB9IA0KICAgICAgaWYgKG5yb3codmFyKSE9MCl7DQogICAgICAgIGRmPC0gY2JpbmQoZGYsdmFyKSANCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGRmIDwtIGNiaW5kKGRmLGMocmVwKCJTWVNURU06IE5vIGRhdGEgYXZhaWxhYmxlIGluIHRhYmxlIixJbi5uLnNlbnRlbmNlcykpKQ0KICAgICAgfQ0KICB9DQogIGRmPC0gZGZbLC0xXQ0KICBjb2xuYW1lcyhkZik8LSBuYW1lcyhJbi5Hcm91cCkNCiAgZGYNCn0NCmBgYA0KDQojIyMjIFdvcmQgQ2xvdWQNCmBgYHtyfQ0KIyBXb3JkLkNsb3VkLkdyb3VwDQojIENyZWF0IFdvcmQgQ2xvdWQgZm9yIGdyb3VwcyBpbiBJbi5Hcm91cA0KV29yZC5DbG91ZC5Hcm91cCA8LSBmdW5jdGlvbihJbi5saXN0PWluYXVnLmxpc3QsIEluLkdyb3VwKXsNCiAgZm9yIChpIGluIDE6bGVuZ3RoKEluLkdyb3VwKSl7DQogICAgICB2YXIgPC0gSW4ubGlzdCU+JQ0KICAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChJbi5Hcm91cFtpXSkpDQogICAgICANCiAgICAgIGRvY3MgPC0gQ29ycHVzKFZlY3RvclNvdXJjZSh2YXIkRnVsbHRleHQpKQ0KICAgICAgZG9jcyA8LSB0bV9tYXAoZG9jcywgc3RyaXBXaGl0ZXNwYWNlKSAjIEVsaW1pbmF0ZSBleHRyYSB3aGl0ZXNwYWNlDQogICAgICBkb2NzIDwtIHRtX21hcChkb2NzLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKSAjIENvbnZlcnQgdG8gbG93ZXIgY2FzZQ0KICAgICAgZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKSAjIFJlbW92ZSBzdG9wd29yZHMNCiAgICAgIGRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVdvcmRzLCBjaGFyYWN0ZXIoMCkpDQogICAgICBkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVQdW5jdHVhdGlvbikgIyBSZW1vdmUgcHVuY3R1YXRpb25zDQogICAgICAjZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlTnVtYmVycykgIyBSZW1vdmUgbnVtYmVycw0KICAgICAgI2RvY3MgPC0gdG1fbWFwKGRvY3MsIHN0ZW1Eb2N1bWVudCkgIyBTdGVtIGRvY3VtZW50DQogICAgICANCiAgICAgIHRkbSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoZG9jcykNCiAgICAgIHRkbS50aWR5IDwtIHRpZHkodGRtKQ0KICAgICAgdGRtLnZhciA8LSBzdW1tYXJpc2UoZ3JvdXBfYnkodGRtLnRpZHksIHRlcm0pLCBzdW0oY291bnQpKQ0KICAgICAgDQogICAgICBwYWwgPC0gYygiUmVkcyIsIkJsdWVzIiwiR3JlZW5zIiwNCiAgICAgICAgICAgICAgICJQdXJwbGVzIiwiT3JhbmdlcyIsICJHcmV5cyIpDQogICAgICANCiAgICAgIHdvcmRjbG91ZCh0ZG0udmFyJHRlcm0sIHRkbS52YXIkYHN1bShjb3VudClgLA0KICAgICAgICAgICAgICAgIHNjYWxlPWMoNSwwLjUpLA0KICAgICAgICAgICAgICAgIG1heC53b3Jkcz0xMDAsDQogICAgICAgICAgICAgICAgbWluLmZyZXE9MSwNCiAgICAgICAgICAgICAgICByYW5kb20ub3JkZXI9RkFMU0UsDQogICAgICAgICAgICAgICAgcm90LnBlcj0wLjMsDQogICAgICAgICAgICAgICAgdXNlLnIubGF5b3V0PVQsDQogICAgICAgICAgICAgICAgcmFuZG9tLmNvbG9yPUZBTFNFLA0KICAgICAgICAgICAgICAgIGNvbG9ycz1icmV3ZXIucGFsKDUsIHBhbFtpXSkNCiAgICAgICAgICApDQogICAgICB0aXRsZShtYWluID0gbmFtZXMoSW4uR3JvdXApW2ldLCANCiAgICAgICAgICAgIGNleC5tYWluPTEuNSwNCiAgICAgICAgICAgIGNvbC5tYWluPWJyZXdlci5wYWwoNSwgcGFsW2ldKVs0XQ0KICAgICAgICAgICApDQogIH0NCn0NCmBgYA0KIyMjIyBTZW50aW1lbnRhbCBBbmFseXNpcyAgDQpgYGB7cn0NCiMgU2VudGVuY2UuTGVuZ3RoLlNlbnRpbWV0YWwuUGxvdA0KIyBDcmVhdGUgUGxvdCBvZiB3b3JkIGNvdW50IG9mIGV2ZXJ5IHNlbnRlbmNlcyBpbiBhbiBpbmRpdmlkdWFsIHNwZWVjaA0KU2VudGVuY2UuTGVuZ3RoLlNlbnRpbWV0YWwuUGxvdCA8LSBmdW5jdGlvbihJbi5saXN0PXNlbnRlbmNlLmxpc3QsIEluLkZpbGUsIEluLlRlcm0pew0KICAjIFRvcCBFbW90aW9uIFZhbHVlDQogIEluLmxpc3QkdG9wZW1vdGlvbi52IDwtIGFwcGx5KHNlbGVjdChJbi5saXN0LCBhbmdlcjpwb3NpdGl2ZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLCBtYXgpDQogIHRlbXAgPC0gSW4ubGlzdCR0b3BlbW90aW9uLnYNCiAgSW4ubGlzdCR0b3BlbW90aW9uLnZbdGVtcDwwLjA1XSA8LSAxDQogIA0KICAjIFRvcCBFbW90aW9uIExvY2F0aW9uDQogIEluLmxpc3QkdG9wZW1vdGlvbiA8LSBhcHBseShzZWxlY3QoSW4ubGlzdCwgYW5nZXI6cG9zaXRpdmUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsIHdoaWNoLm1heCkNCiAgSW4ubGlzdCR0b3BlbW90aW9uW0luLmxpc3QkdG9wZW1vdGlvbi52PDAuMDVdIDwtIDANCiAgSW4ubGlzdCR0b3BlbW90aW9uIDwtIEluLmxpc3QkdG9wZW1vdGlvbiArIDENCiANCiAgIyBGaWx0ZXIgYW5kIFNlbGVjdCBmcm9tIEluLmxpc3QNCiAgZGYgPC0gSW4ubGlzdCU+JQ0KICAgICAgICBmaWx0ZXIoRmlsZT09SW4uRmlsZSwgDQogICAgICAgICAgICAgICBUZXJtPT1Jbi5UZXJtKSU+JQ0KICAgICAgICBzZWxlY3Qoc2VudC5pZCwgd29yZC5jb3VudCwgDQogICAgICAgICAgICAgIHRvcGVtb3Rpb24sIHRvcGVtb3Rpb24udikNCiAgDQogICMgU2V0IGNvbG9yIGZvciB0aGUgcGxvdA0KICBjb2wudXNlIDwtIGJyZXdlci5wYWwoMTAsIlNldDMiKQ0KICBwdGNvbC51c2UgPC0gYWxwaGEoY29sLnVzZVtkZiR0b3BlbW90aW9uXSwgDQogICAgICAgICAgICAgICAgICAgICBzcXJ0KHNxcnQoc3FydChkZiR0b3BlbW90aW9uLnYpKSkpDQogIA0KICAjIFBsb3QNCiAgcGxvdChkZiRzZW50LmlkLCBkZiR3b3JkLmNvdW50LCANCiAgICAgICBjb2w9cHRjb2wudXNlLA0KICAgICAgIHR5cGU9ImgiICMseWxpbT1jKC0xMCwgbWF4KEluLmxpc3Qkd29yZC5jb3VudCkpDQogICAgICAgKQ0KICB0aXRsZShtYWluID1wYXN0ZShJbi5GaWxlLCAiVGVybSIsIEluLlRlcm0sIHNlcD0iICIpLCANCiAgICAgICAgeGxhYiA9ICJTZW50ZW5jZSBJRCIsIHlsYWIgPSAiTnVtYmVyIG9mIFdvcmRzIikNCiAgbGVnZW5kKCJ0b3ByaWdodCIsIGluc2V0PSAwLjAyLCANCiAgICAgICAgIGxlZ2VuZCA9YygiYW5nZXIiLCJhbnRpY2lwYXRpb24iLCJkaXNndXN0IiwgImZlYXIiLCAiam95IiwNCiAgICAgICAgICAgICAgICAgICAic2FkbmVzcyIsInN1cHJpc2UiLCJ0cnVzdCIsIm5lZ2F0aXZlIiwicG9zaXRpdmUiKSwgDQogICAgICAgICB0ZXh0LmNvbCA9IGJyZXdlci5wYWwoMTAsIlNldDMiKSwgDQogICAgICAgICBjZXggPSAwLjYsIGJveC5sdHk9MiApDQp9DQoNCmBgYA0KDQpgYGB7cn0NCiMgU2VudGltZW50YWwuQW5hbHlzaXMuUGxvdHMgDQojIENyZWF0IEhlYXRtYXAgZm9yIGNvcnJlbGF0aW9ucyBhbmQgDQojIEJhcnBsb3QgZm9yIGF2ZXJhZ2UgdmFsdWUgb2YgZW1vdGlvbnMgZm9yIGdyb3VwcyBpbiBJbi5Hcm91cA0KU2VudGltZW50YWwuQW5hbHlzaXMuUGxvdHMgPC0gZnVuY3Rpb24oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCBJbi5Hcm91cCl7DQpmb3IgKGkgaW4gMTpsZW5ndGgoSW4uR3JvdXApKXsNCiMgIGhlYXRtYXAuMihjb3IoSW4ubGlzdCU+JQ0KIyAgICAgICAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoSW4uR3JvdXBbaV0pKSU+JQ0KIyAgICAgICAgICAgICAgICBzZWxlY3QoYW5nZXI6dHJ1c3QpKSwgDQojICAgICAgICAgIHNjYWxlID0gIm5vbmUiLCANCiMgICAgICAgICAgY29sID0gYmx1ZXJlZCgxMDApLCBtYXJnaW49Yyg2LCA2KSwga2V5PUYsDQojICAgICAgICAgIHRyYWNlID0gIm5vbmUiLCBkZW5zaXR5LmluZm8gPSAibm9uZSIpDQoNCiAgZW1vLm1lYW5zLnZhciA8LSBjb2xNZWFucyhJbi5saXN0JT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEZpbGUlaW4ldW5saXN0KEluLkdyb3VwW2ldKSklPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoYW5nZXI6dHJ1c3QpPjAuMDEpDQoNCiAgYmFycGxvdChlbW8ubWVhbnMudmFyW29yZGVyKGVtby5tZWFucy52YXIpXSwgDQogICAgICAgIGxhcz0yLCBjb2w9YnJld2VyLnBhbCg4LCJQYXN0ZWwyIilbb3JkZXIoZW1vLm1lYW5zLnZhcildLCANCiAgICAgICAgaG9yaXo9VCwgbWFpbj1uYW1lcyhJbi5Hcm91cClbaV0sIHhsYWIgPSAiQXZlcmFnZSBWYWx1ZSBvZiBFbW90aW9ucyIpDQp9DQp9DQpgYGANCg0KYGBge3J9DQojIFNlbnRpbWVudGFsLlNlbnRlbmNlLlNlYXJjaGVyDQojIFNlYXJjaCBmb3Igc2VudGVuY2VzIHRoYXQgaGF2ZSB0aGUgbWF4IHZhbHVlIG9mIGVhY2ggb2YgdGhlIDggZW1vdGlvbnMgZm9yIGdyb3VwcyBpbiBJbi5Hcm91cA0KDQpTZW50aW1lbnRhbC5TZW50ZW5jZS5TZWFyY2hlciA8LSBmdW5jdGlvbihJbi5saXN0PXNlbnRlbmNlLmxpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uR3JvdXAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4uVGVybT1jKCIxIiwiMiIsIjMiLCI0IiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW4ubWluLndvcmRzPTMpew0KICBkZiA8LSBOVUxMDQogIGZvciAoaSBpbiAxOmxlbmd0aChJbi5Hcm91cCkpew0KICAgIHZhciA8LSAgIEluLmxpc3QlPiUNCiAgICAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoSW4uR3JvdXBbaV0pLCANCiAgICAgICAgICAgICAgICAgICAgVGVybSVpbiVJbi5UZXJtLA0KICAgICAgICAgICAgICAgICAgICB3b3JkLmNvdW50Pj1Jbi5taW4ud29yZHMpJT4lDQogICAgICAgICAgICAgICBzZWxlY3Qoc2VudGVuY2VzOnRydXN0KQ0KIyAgICB2YXIgPC0gYXMuZGF0YS5mcmFtZSh2YXIpDQogICAgcnN0IDwtIGFzLmNoYXJhY3Rlcih2YXIkc2VudGVuY2VzW2FwcGx5KHZhclssLSgxOjIpXSwyLHdoaWNoLm1heCldKQ0KICAgICAgaWYgKG5yb3codmFyKSE9MCl7DQogICAgICAgIGRmPC0gY2JpbmQoZGYscnN0KSANCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGRmIDwtIGNiaW5kKGRmLGMocmVwKCJTWVNURU06IE5vIGRhdGEgYXZhaWxhYmxlIGluIHRhYmxlIiw4KSkpDQogICAgICB9DQogICAgfQ0KICBjb2xuYW1lcyhkZik8LSBuYW1lcyhJbi5Hcm91cCkNCiAgcm93bmFtZXMoZGYpIDwtIGMoImFuZ2VyIiwiYW50aWNpcGF0aW9uIiwiZGlzZ3VzdCIsImZlYXIiLA0KICAgICAgICAgICAgICAgICAgICAgImpveSIsInNhZG5lc3MiLCJzdXJwcmlzZSIsInRydXN0IikNCiAgZGYNCn0NCmBgYA0KDQpgYGB7cn0NCiMgU2VudGltZW50YWwuU2VudGVuY2UuS21lYW5zDQojIFBlcmZvcm0gay1tZWFucyBjbHVzdGVyaW5nIGZvciBhIGdyb3VwIEluLmdyb3VwDQpTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMgPC0gZnVuY3Rpb24oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLmdyb3VwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluLlRlcm09YygiMSIsIjIiLCIzIiwiNCIpKXsNCiAgICB2YXIgPC0gICBJbi5saXN0JT4lDQogICAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChJbi5ncm91cCksIA0KICAgICAgICAgICAgICAgICAgICAgVGVybSVpbiVJbi5UZXJtKSU+JQ0KICAgICAgICAgICAgICBncm91cF9ieShGaWxlKSU+JQ0KICAgICAgICAgICAgICBzdW1tYXJpc2UoYW5nZXI9bWVhbihhbmdlciksDQogICAgICAgICAgICAgICAgICAgICAgICBhbnRpY2lwYXRpb249bWVhbihhbnRpY2lwYXRpb24pLA0KICAgICAgICAgICAgICAgICAgICAgICAgZGlzZ3VzdD1tZWFuKGRpc2d1c3QpLA0KICAgICAgICAgICAgICAgICAgICAgICAgZmVhcj1tZWFuKGZlYXIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgam95PW1lYW4oam95KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHNhZG5lc3M9bWVhbihzYWRuZXNzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN1cnByaXNlPW1lYW4oc3VycHJpc2UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgdHJ1c3Q9bWVhbih0cnVzdCkNCiAgICAgICAgICAgICAgICAgICAgICAgICNuZWdhdGl2ZT1tZWFuKG5lZ2F0aXZlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICNwb3NpdGl2ZT1tZWFuKHBvc2l0aXZlKQ0KICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgIHZhciA8LSBhcy5kYXRhLmZyYW1lKHZhcikNCiAgICBmb3IoayBpbiAyOjkpew0KICAgICAgdmFyWyxrXVtpcy5uYSh2YXJbLGtdKV0gPSAwDQogICAgfQ0KICAgIHJvd25hbWVzKHZhcikgPC0gYXMuY2hhcmFjdGVyKHZhclssMV0pDQogICAgDQogICAgcHJlc2lkLnN1bW1hcnkgPC0gdmFyDQogICAgDQogICAga20ucmVzIDwtIGttZWFucyh2YXJbLC0xXSwgaXRlci5tYXg9MjAwLCA0KQ0KICAgIA0KICAgIHJldHVybihsaXN0KGttLnJlcyxwcmVzaWQuc3VtbWFyeSkpDQogICAgDQp9DQoNCmBgYA0KDQoNCiMjIEFuYWx5c2lzICANCg0KKipHcm91cHMgb2YgSW50ZXJlc3QqKiAgDQpUaGUgZm9sbG93aW5nIGZhY3RvcnMgd2VyZSBjb25zaWRlcmVkIGluIHRoaXMgYW5hbHlzaXMuICANCg0KKiAgIFsqKk92ZXJhbCBhbmQgSW5kaXZpZHVhbCBTcGVlY2hlcyoqXSgjSW5kaXZTcGVlY2hlcykgIA0KKiAgIFsqKk51bWJlciBvZiBUZXJtcyBTZXJ2ZWQqKl0oI1Rlcm1zU2VydmVkKSAgDQoqICAgWyoqUG9saXRpY2FsIFBhcnR5KipdKCNQb2xpdGljYWxQYXJ0eSkgDQoqICAgWyoqRWR1Y2F0aW9uYWwgQmFja2dyb3VuZCoqXSgjRWR1Y2F0aW9uYWxCYWNrZ3JvdW5kKSANCiogICBbKipDYXJlZXIgUHJpb3IgdG8gUG9saXRpY3MqKl0oI0NhcmVlckJhY2tncm91bmQpICANCiogICBbKipTZXJ2ZWQgRHVyaW5nIFdhciBFcmE/KipdKCNXYXJFcmEpICANCg0KKipUeXBlcyBvZiBBbmFseXNpcyoqICAgDQpGb3IgZWFjaCBncm91cCBvZiBpbnRlcmVzdCwgd2Ugd2lsbCBwZXJmb3JtIHRoZSBmb2xsb3dpbmcgYW5hbHlzaXMuICANCg0KKiAgIExlbmd0aCBvZiBTcGVlY2hlcyAoTnVtYmVyIG9mIFdvcmRzIGluIFNwZWVjaGVzKSAgDQoqICAgTGVuZ3RoIG9mIFNlbnRlbmNlcyANCiogICBXb3JkIENsb3VkICANCiogICBTZW50aW1lbnRhbCBBbmFseXNpcyAgDQoqICAgVG9waWMgTW9kZWxpbmcgIA0KDQojIyMgT3ZlcmFsIGFuZCBJbmRpdmlkdWFsIFNwZWVjaGVzICA8YSBuYW1lPSJJbmRpdlNwZWVjaGVzIj48L2E+ICB7LnRhYnNldH0gIA0KIyMjIyBPdmVydmlldyAgDQpUaGUgc2NvcGUgb2YgdGhpcyBhbmFseXNpcyBpbmNsdWRlcyA1OCBJbmF1Z3VyYWwgU3BlZWNoZXMgb2YgMzkgUE9UVVMsIHdoaWNoIGFyZSBzY3JhcHBlZCBmcm9tIFtUaGUgQW1lcmljYW4gUHJlc2lkZW50Y3kgUHJvamVjdF1bMV0uIFdlIHdpbGwgcGVyZm9ybSBzb21lIGFuYWx5c2lzIG92ZXIgYWxsIHRoZXNlIHNwZWVjaGVzIGhvcGVmdWxseSB0byBsZWFybiBzb21lIG5ldyBmYWN0cyBhYm91dCB0aGVtLiBXZSBwbGFuIHRvIGZpbmQgb3V0IHRoZSBsZW5ndGggb2YgdGhlc2Ugc3BlZWNoZXMsIHRoZSBsZW5ndGggb2YgdGhlIHNlbnRlbmNlcyB1c2VkIGluIGEgc3BlZWNoLCBhbmQgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCB3b3Jkcy4gQW1vbmcgYWxsIHRoZXNlIHNwZWVjaGVzLCBmaXZlIGluYXVndXJhbCBhZGRyZXNzZXMgd2VyZSBjb25zaWRlcmVkIHRoZSBiZXN0IG9mIGFsbCB0aW1lLCB3aGljaCBhcmUgVGhvbWFzIEplZmZlcnNvbidzIDFzdCAoMTgwMSksIEFicmFoYW0gTGluY29sbidzIDJuZCAoMTg2NSksIEZyYW5rbGluIFJvb3NldmVsdCdzIDFzdCAoMTkzMyksIEZyYW5rbGluIFJvb3NldmVsdCdzIDJuZCAoMTkzNyksIGFuZCBKb2huIEYuIEtlbm5lZHkncyAoMTk2MSkuIFdlIHdpbGwgdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZW0gYW5kIHNlZSBpZiB3ZSBjYW4gZmluZCBhbnkgaW50ZXJlc3RpbmcgZmFjdHMsIHNpbWlsYXJpZXMgb3IgZGlmZmVyZW5jZXMsIGFtb25nIHRoZXNlIGZpdmUgc3BlZWNoZXMsIHdoaWNoIG1heSBjb250cmlidXRlIHRvIHRoZSBncmVhdCBzdWNjZXNzIG9mIHRoZW0uIEluIGFkZGl0aW9ubiwgdGhlIG1vc3QgcmVjZW50IGZvdXIgUE9UVVMgbWF5IHNvdW5kIG1vcmUgZmFtaWxpYXIgdGhhbiBvdGhlcnMgdG8gb3VyIE1pbGxlbm5pYWxzLiBTbyBsZXQncyB0YWtlIHRoZWlyIHNwZWVjaGVzIGludG8gb3VyIGNvbnNpZGVyYXRpb24gYXMgcGFydCBvZiB0aGUgdGhlIGFuYWx5c2lzIG9mIGluZGl2aWR1YWwgc3BlZWNuIGFzIHdlbGwuICANCg0KIyMjIyBTdGFnaW5nICANCipTdGFnaW5nIGZvciBXb3JkIENsb3VkKg0KYGBge3J9DQojIFJlYWQgaW4gYWxsIHRoZSBzcGVlY2hlcyAgDQpmb2xkZXIucGF0aCA8LSAiLi4vZGF0YS9JbmF1Z3VyYWxTcGVlY2hlcy8iDQpzcGVlY2hlcyA8LSBsaXN0LmZpbGVzKHBhdGggPSBmb2xkZXIucGF0aCwgcGF0dGVybiA9ICIqLnR4dCIpDQojcHJleC5vdXQgPC0gc3Vic3RyKHNwZWVjaGVzLCA2LCBuY2hhcihzcGVlY2hlcyktNCkNCmRvY3MgPC0gQ29ycHVzKERpclNvdXJjZShmb2xkZXIucGF0aCkpDQojZG9jcyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKGluYXVnLmxpc3QkRnVsbHRleHQpKQ0KDQojIENsZWFuIHRoZSB0ZXh0IGRvY3VtZW50DQpkb2NzIDwtIHRtX21hcChkb2NzLCBzdHJpcFdoaXRlc3BhY2UpICMgRWxpbWluYXRlIGV4dHJhIHdoaXRlc3BhY2UNCmRvY3MgPC0gdG1fbWFwKGRvY3MsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICMgQ29udmVydCB0byBsb3dlciBjYXNlDQpkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpICMgUmVtb3ZlIHN0b3B3b3Jkcw0KZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlV29yZHMsIGNoYXJhY3RlcigwKSkNCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVB1bmN0dWF0aW9uKSAjIFJlbW92ZSBwdW5jdHVhdGlvbnMNCiNkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVOdW1iZXJzKSAjIFJlbW92ZSBudW1iZXJzDQojZG9jcyA8LSB0bV9tYXAoZG9jcywgc3RlbURvY3VtZW50KSAjIFN0ZW0gZG9jdW1lbnQNCg0KdGRtLmFsbCA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoZG9jcykNCnRkbS50aWR5IDwtIHRpZHkodGRtLmFsbCkNCnRkbS5vdmVyYWxsIDwtIHN1bW1hcmlzZShncm91cF9ieSh0ZG0udGlkeSwgdGVybSksIHN1bShjb3VudCkpDQoNCiMgR2VuZXJhdGUgYFRGLUlERiBXZWlnaHRlZCBEb2N1bWVudC1UZXJtIE1hdHJpY2VzYCAgDQpkdG0uYWxsIDwtIERvY3VtZW50VGVybU1hdHJpeChkb2NzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxpc3Qod2VpZ2h0aW5nID0gZnVuY3Rpb24oeCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodFRmSWRmKHgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemUgPUZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3B3b3JkcyA9IFRSVUUpKQ0KZHRtLnRpZHkgPC0gdGlkeShkdG0uYWxsKQ0KI2R0bS5hbGwgPC0gRG9jdW1lbnRUZXJtTWF0cml4KGRvY3MpDQojIENvbnZlcnQgcm93bmFtZXMgdG8gZmlsZW5hbWVzDQojcm93bmFtZXMoZHRtLmFsbCkgPC0gcGFzdGUoY29ycHVzLmxpc3QkRmlsZSwgY29ycHVzLmxpc3QkVGVybSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycHVzLmxpc3Qkc2VudC5pZCwgc2VwPSJfIikNCg0KI3Jvd1RvdGFscyA8LSBhcHBseShkdG0uYWxsICwgMSwgc3VtKSAjRmluZCB0aGUgc3VtIG9mIHdvcmRzIGluIGVhY2ggRG9jdW1lbnQNCiNkdG0uYWxsICA8LSBkdG0uYWxsW3Jvd1RvdGFscz4gMCwgXQ0KI2NvcnB1cy5saXN0IDwtIGNvcnB1cy5saXN0W3Jvd1RvdGFscz4wLCBdDQpgYGANCipTdGFnaW5nIGZvciBMREEgVG9waWMgTW9kZWxpbmcqIA0KYGBge3J9DQojIENyZWF0ZSBjb3JwdXMgbGlzdA0KY29ycHVzLmxpc3QgPC0gc2VudGVuY2UubGlzdFsyOihucm93KHNlbnRlbmNlLmxpc3QpLTEpLCBdDQpzZW50ZW5jZS5wcmUgPC0gc2VudGVuY2UubGlzdCRzZW50ZW5jZXNbMToobnJvdyhzZW50ZW5jZS5saXN0KS0yKV0NCnNlbnRlbmNlLnBvc3QgPC0gc2VudGVuY2UubGlzdCRzZW50ZW5jZXNbMzoobnJvdyhzZW50ZW5jZS5saXN0KS0xKV0NCg0KY29ycHVzLmxpc3Qkc25pcGV0cyA8LSBwYXN0ZShzZW50ZW5jZS5wcmUsIGNvcnB1cy5saXN0JHNlbnRlbmNlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnRlbmNlLnBvc3QsIHNlcD0iICIpDQpybS5yb3dzPSgxOm5yb3coY29ycHVzLmxpc3QpKVtjb3JwdXMubGlzdCRzZW50LmlkPT0xXQ0Kcm0ucm93cz1jKHJtLnJvd3MsIHJtLnJvd3MtMSkNCmNvcnB1cy5saXN0PWNvcnB1cy5saXN0Wy1ybS5yb3dzLCBdDQoNCmRvY3MgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShjb3JwdXMubGlzdCRzbmlwZXRzKSkNCiN3cml0ZUxpbmVzKGFzLmNoYXJhY3Rlcihkb2NzW1tzYW1wbGUoMTpucm93KGNvcnB1cy5saXN0KSwgMSldXSkpDQoNCiMgQ2xlYW4gdGhlIHRleHQgZG9jdW1lbnQNCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHN0cmlwV2hpdGVzcGFjZSkgIyBFbGltaW5hdGUgZXh0cmEgd2hpdGVzcGFjZQ0KZG9jcyA8LSB0bV9tYXAoZG9jcywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkgIyBDb252ZXJ0IHRvIGxvd2VyIGNhc2UNCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkgIyBSZW1vdmUgc3RvcHdvcmRzDQpkb2NzIDwtIHRtX21hcChkb2NzLCByZW1vdmVXb3JkcywgY2hhcmFjdGVyKDApKQ0KZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlUHVuY3R1YXRpb24pICMgUmVtb3ZlIHB1bmN0dWF0aW9ucw0KZG9jcyA8LSB0bV9tYXAoZG9jcywgcmVtb3ZlTnVtYmVycykgIyBSZW1vdmUgbnVtYmVycw0KZG9jcyA8LSB0bV9tYXAoZG9jcywgc3RlbURvY3VtZW50KSAjIFN0ZW0gZG9jdW1lbnQNCg0KIyBHZW5lcmF0ZSBgVEYtSURGIFdlaWdodGVkIERvY3VtZW50LVRlcm0gTWF0cmljZXNgICANCiNkdG0uYWxsLmxkYSA8LSBEb2N1bWVudFRlcm1NYXRyaXgoZG9jcywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KHdlaWdodGluZyA9IGZ1bmN0aW9uKHgpDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0VGZJZGYoeCwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemUgPUZBTFNFKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3B3b3JkcyA9IFRSVUUpKQ0KZHRtLmFsbC5sZGEgPC0gRG9jdW1lbnRUZXJtTWF0cml4KGRvY3MpDQojIENvbnZlcnQgcm93bmFtZXMgdG8gZmlsZW5hbWVzDQpyb3duYW1lcyhkdG0uYWxsLmxkYSkgPC0gcGFzdGUoY29ycHVzLmxpc3QkRmlsZSwgY29ycHVzLmxpc3QkVGVybSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JwdXMubGlzdCRzZW50LmlkLCBzZXA9Il8iKQ0KI0ZpbmQgdGhlIHN1bSBvZiB3b3JkcyBpbiBlYWNoIERvY3VtZW50IA0Kcm93VG90YWxzIDwtIGFwcGx5KGR0bS5hbGwubGRhICwgMSwgc3VtKSANCmR0bS5hbGwubGRhICA8LSBkdG0uYWxsLmxkYVtyb3dUb3RhbHM+IDAsIF0NCmNvcnB1cy5saXN0IDwtIGNvcnB1cy5saXN0W3Jvd1RvdGFscz4wLCBdDQpgYGANCg0KIyMjIyBMZW5ndGggb2YgU3BlZWNoZXMgIA0KQmVsb3cgaXMgYSBzdW1tYXJ5IG9mIHRoZSBsZW5ndGggb2YgYWxsIHNwZWVjaGVzLiANCmBgYHtyfQ0Kc3VtbWFyeShpbmF1Zy5saXN0JFdvcmRzKQ0KYGBgDQpUbyBnZXQgYSBiZXR0ZXIgc2Vuc2Ugb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtYmVyIG9mIHdvcmRzIGluIHNwZWVjaCwgYSBib3gtYW5kLXdoaXNrZXIgcGxvdCBpcyBjcmVhdGVkIGFzIGJlbG93LiBBcyBzaG93biwgdGhlIHNwZWVjaGVzIG9mIFdpbGxpYW0gSC4gSGFycmlzb24gYW5kIFdpbGxpYW0gSC4gVGFmdCBzcG9rZSB3ZXJlIG11Y2ggbG9uZ2VyIHRoYW4gb3RoZXJzLiAgDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NX0NCiMgR2VuZXJhdGUgYmFyIHBsb3Qgb2YgdGhlIGxlbmd0aCBvZiBzcGVlY2hlcw0KYnhwZGF0LnNwZWVjaCA8LSBib3hwbG90KGluYXVnLmxpc3QkV29yZHMsIHhsYWI9Ik51bWJlciBvZiBXb3JkcyBpbiBhIFNwZWVjaCIpDQp0ZXh0KHg9MS4yLCB5PWJ4cGRhdC5zcGVlY2gkb3V0LCANCiAgICAgbGFiZWxzPWluYXVnLmxpc3QkUHJlc2lkZW50W3doaWNoKGluYXVnLmxpc3QkV29yZHMlaW4lYnhwZGF0LnNwZWVjaCRvdXQpXSwgDQogICAgIGNleD0wLjcsIGNvbD0iYmx1ZSIpDQpzcGVlY2gubGVuLmZpbGUgPC0gaW5hdWcubGlzdCRGaWxlW29yZGVyKGluYXVnLmxpc3QkV29yZHMsIGRlY3JlYXNpbmcgPVRSVUUpXQ0KYGBgDQoNCkJlbG93IGxpc3RlZCB0aGUgdG9wIHRocmVlIFBPVFVTIHdobyBoYWQgdGhlIGxvbmdlc3QgYW5kIHNob3J0ZXN0IHNwZWVjaGVzIHJlc3BlY3RpdmVseS4gIA0KVGhlIHRvcCB0aHJlZSBQT1RVUyB3aG8gaGFkIHRoZSBsb25nZXN0IHNwZWVjaGVzIGFyZSANCmBgYHtyfQ0KaGVhZChzcGVlY2gubGVuLmZpbGUsMykNCmBgYA0KVGhlIHRvcCB0aHJlZSBQT1RVUyB3aG8gaGFkIHRoZSBzaG9ydGVzdCBzcGVlY2hlcyBhcmUNCmBgYHtyfQ0KdGFpbChzcGVlY2gubGVuLmZpbGUsMykNCmBgYA0KDQojIyMjIExlbmd0aCBvZiBTZW50ZW5jZXMgDQpCZWxvdyBpcyBhIHN1bW1hcnkgb2YgdGhlIGxlbmd0aCBvZiBzZW50ZW5jZXMgdXNlZCBpbiBhbiBpbmF1Z3VyYWwgc3BlZWNoLg0KYGBge3J9DQojIFN1bW1hcnkgb2YgdGhlIGxlbmd0aCBvZiBzZW50ZW5jZXMNCnN1bW1hcnkoc2VudGVuY2UubGlzdCR3b3JkLmNvdW50KQ0KYGBgDQpgciBzZW50ZW5jZS5saXN0JFByZXNpZGVudFt3aGljaC5tYXgoc2VudGVuY2UubGlzdCR3b3JkLmNvdW50KV1gIGhhZCB0aGUgbG9uZ2VzdCBzZW50ZW5jZSB3aXRoIGByIG1heChzZW50ZW5jZS5saXN0JHdvcmQuY291bnQpYCB3b3JkcyBpbiBoaXMgc3BlZWNoLiBCZWxvdyBsaXN0ZWQgdGhlIHRvcCB0aHJlZSBzaG9ydGVzdCwgd2hpY2ggaGF2ZSBiZWVuIGZpbHRlciB0byBiZSBsb25nZXIgdGhhbiB0aHJlZSB3b3JkcywgYW5kIHRoZSBsb25nZXN0IHNlbnRlbnNlcyBpbiBhbGwgc3BlZWNoZXMuICANCmBgYHtyfQ0Kc2VudGVuY2UubGVuLmZpbGUgPC0gc2VudGVuY2UubGlzdCU+JQ0KICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcih3b3JkLmNvdW50Pj0zKSU+JQ0KICAgICAgICAgICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyh3b3JkLmNvdW50KSklPiUNCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc2VudGVuY2VzKQ0KdGFpbChzZW50ZW5jZS5sZW4uZmlsZSw1KQ0KaGVhZChzZW50ZW5jZS5sZW4uZmlsZSw1KQ0KYGBgDQoNCkEgYm94LWFuZC13aGlza2VyIHBsb3QgaGFzIGJlZW4gY3JlYXRlZCBiZWxvdyB0byBoZWxwIHVzIGdldCBhIGJldHRlciBzZW5zZSBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBudW1iZXIgb2Ygd29yZHMgaW4gYSBzZW50ZW5jZS4gDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NX0NCndvcmQuY291bnQuYXZlIDwtIHNlbnRlbmNlLmxpc3QlPiVncm91cF9ieShQcmVzaWRlbnQpJT4lc3VtbWFyaXNlKHdvcmQuY291bnQ9cm91bmQobWVhbih3b3JkLmNvdW50LDApKSkNCmJ4cGRhdC5zZW50ZW5jZSA8LSBib3hwbG90KHdvcmQuY291bnQuYXZlJHdvcmQuY291bnQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxhYj0iTnVtYmVyIG9mIFdvcmRzIGluIGEgU2VudGVuY2UiKQ0KdGV4dCh4PTEuMiwgeT1ieHBkYXQuc2VudGVuY2Ukb3V0LCBjZXg9MC41LCBjb2w9ImJsdWUiKQ0KYGBgDQoNCg0KIyMjIyBXb3JkIENsb3VkICANClRoZSB0ZXh0IHdpdGggbGFyZ2VyIGluIHNpemUgYW5kIGRhcmtlciBpbiBjb2xvciBhcyBzaG93biBpbiB0aGUgd29yZHMgY2xvdWQgYmVsb3cgYXJlIHRoZSB3b3JkcyB0aGF0IGFwcGVhcmVkIG1vcmUgb2Z0ZW4gaW4gYWxsIGluYXVndXJhbCBzcGVlY2hlcy4gV2l0aCBubyBzdXJwcmlzZSAsIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgd29yZHMgaW4gYWxsIHNwZWVjaGVzIGluY2x1ZGUgIndpbGwiLCAiZ292ZXJubWVudCIsIGFuZCAicGVvcGxlIi4gDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NX0NCnNldC5zZWVkKDEyMykNCndvcmRjbG91ZCh0ZG0ub3ZlcmFsbCR0ZXJtLCB0ZG0ub3ZlcmFsbCRgc3VtKGNvdW50KWAsDQogICAgICAgICAgc2NhbGU9Yyg1LDAuNSksDQogICAgICAgICAgbWF4LndvcmRzPTEwMCwNCiAgICAgICAgICBtaW4uZnJlcT0xLA0KICAgICAgICAgIHJhbmRvbS5vcmRlcj1GQUxTRSwNCiAgICAgICAgICByb3QucGVyPTAuMywNCiAgICAgICAgICB1c2Uuci5sYXlvdXQ9VCwNCiAgICAgICAgICByYW5kb20uY29sb3I9RkFMU0UsDQogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoNSwiQmx1ZXMiKQ0KICAgICAgICAgICkgDQp0aXRsZShtYWluPSJXaGF0IEFyZSBUaGUgTW9zdCBDb21tb24gV29yZHMgaW4gSW5hdWd1cmFsIFNwZWVjaGVzPyIsIGNleC5tYWluPTAuOSkNCmBgYA0KQW4gaW50ZXJhY3RpdmUgd29yZGNsb3VkIGhhcyBiZWVuIGNyZWF0ZWQgdG8gaGVscCB1cyBwaWN0dXJlIHRoZSBtb3N0IGZyZXF1ZW50IHdvcmRzIHVzZWQgaW4gZWFjaCBpbmRpdmlkdWFsIHNwZWVjaC4gVGhpcyBjb3VsZCBiZSB1c2VkIHRvIGZpbmQgdGhlIHdvcmRjbG91ZCBvZiBlYWNoIGluZGl2aWR1YWwgc3BlZWNoIGFzIHdlbGwgYXMgdG8gY29tcGFyZSBiZXR3ZWVuIHR3byBkaWZmZXJlbnQgc3BlZWNoZXMuIFdlIHdpbGwgdXNlIHRoaXMgaW50ZXJhY3RpdmUgd29yZGNsb3VkIHRvIGluZGVudGlmeSB0aGUgbW9zdCBjb21tb25seSB1c2VkIHdvcmRzIGZvciB0aGUgbW9zdCBmYW1vdXMgaWF1Z3VyYWwgc3BlZWNoZXMgYXMgd2VsbCBhcyB0aGUgaWF1Z3VyYWwgc3BlZWNoZXMgZ2l2ZW4gYnkgdGhlIGZvdXIgbW9zdCByZWNlbnQgcHJlc2lkZW50cy4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHNoaW55KQ0KDQpzaGlueUFwcCgNCiAgICB1aSA9IGZsdWlkUGFnZSgNCiAgICAgIGZsdWlkUm93KHN0eWxlID0gInBhZGRpbmctYm90dG9tOiAyMHB4OyIsDQogICAgICAgIGNvbHVtbig0LCBzZWxlY3RJbnB1dCgnc3BlZWNoMScsICdTcGVlY2ggMScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BlZWNoZXMsIHNlbGVjdGVkPXNwZWVjaGVzWzVdKSksDQogICAgICAgIGNvbHVtbig0LCBzZWxlY3RJbnB1dCgnc3BlZWNoMicsICdTcGVlY2ggMicsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BlZWNoZXMsIHNlbGVjdGVkPXNwZWVjaGVzWzldKSksDQogICAgICAgIGNvbHVtbig0LCBzbGlkZXJJbnB1dCgnbndvcmRzJywgJ051bWJlciBvZiB3b3JkcycsIDMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gMjAsIG1heCA9IDIwMCwgdmFsdWU9MTAwLCBzdGVwID0gMjApKQ0KICAgICAgKSwNCiAgICAgIGZsdWlkUm93KA0KICAgICAgICBwbG90T3V0cHV0KCd3b3JkY2xvdWRzJywgaGVpZ2h0ID0gIjQwMHB4IikNCiAgICAgICksDQoNCiAgICAgIGZsdWlkUm93KCANCiAgICAgICAgYWN0aW9uQnV0dG9uKCJjbG9zZSIsIkNsb3NlIHdpbmRvdyIsDQogICAgICAgICAgICAgICAgICAgICBvbmNsaWNrID0gInNldFRpbWVvdXQoZnVuY3Rpb24oKXt3aW5kb3cuY2xvc2UoKTt9LDUwMCk7IikNCiAgICAgICAgKQ0KICAgICksDQogICAgDQogICAgc2VydmVyID0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCwgc2Vzc2lvbikgew0KICAgICAgIyBDb21iaW5lIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMgaW50byBhIG5ldyBkYXRhIGZyYW1lDQogICAgICBzZWxlY3RlZERhdGEgPC0gcmVhY3RpdmUoew0KICAgICAgICBsaXN0KGR0bS50ZXJtMT1kdG0udGlkeSR0ZXJtW2R0bS50aWR5JGRvY3VtZW50PT0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIod2hpY2goc3BlZWNoZXMgPT0gaW5wdXQkc3BlZWNoMSkpXSwNCiAgICAgICAgICAgICBkdG0uY291bnQxPWR0bS50aWR5JGNvdW50W2R0bS50aWR5JGRvY3VtZW50PT0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcih3aGljaChzcGVlY2hlcyA9PSBpbnB1dCRzcGVlY2gxKSldLA0KICAgICAgICAgICAgIGR0bS50ZXJtMj1kdG0udGlkeSR0ZXJtW2R0bS50aWR5JGRvY3VtZW50PT0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIod2hpY2goc3BlZWNoZXMgPT0gaW5wdXQkc3BlZWNoMikpXSwNCiAgICAgICAgICAgICBkdG0uY291bnQyPWR0bS50aWR5JGNvdW50W2R0bS50aWR5JGRvY3VtZW50PT0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcih3aGljaChzcGVlY2hlcyA9PSBpbnB1dCRzcGVlY2gyKSldKQ0KICAgICAgfSkNCiAgICAgIA0KICAgICAgb3V0cHV0JHdvcmRjbG91ZHMgPC0gcmVuZGVyUGxvdChoZWlnaHQgPSA0MDAsIHsNCiAgICAgICAgcGFyKG1mcm93PWMoMSwyKSwgbWFyID0gYygwLCAwLCAzLCAwKSkNCiAgICAgICAgd29yZGNsb3VkKHNlbGVjdGVkRGF0YSgpJGR0bS50ZXJtMSwgDQogICAgICAgICAgICAgICAgICBzZWxlY3RlZERhdGEoKSRkdG0uY291bnQxLA0KICAgICAgICAgICAgICBzY2FsZT1jKDQsMC41KSwNCiAgICAgICAgICAgICAgbWF4LndvcmRzPWlucHV0JG53b3JkcywNCiAgICAgICAgICAgICAgbWluLmZyZXE9MSwNCiAgICAgICAgICAgICAgcmFuZG9tLm9yZGVyPUZBTFNFLA0KICAgICAgICAgICAgICByb3QucGVyPTAuMywNCiAgICAgICAgICAgICAgdXNlLnIubGF5b3V0PVQsDQogICAgICAgICAgICAgIHJhbmRvbS5jb2xvcj1GQUxTRSwNCiAgICAgICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoNSwiQmx1ZXMiKSwgDQogICAgICAgICAgICBtYWluPWlucHV0JHNwZWVjaDEpDQogICAgICAgIHdvcmRjbG91ZChzZWxlY3RlZERhdGEoKSRkdG0udGVybTIsIA0KICAgICAgICAgICAgICAgICAgc2VsZWN0ZWREYXRhKCkkZHRtLmNvdW50MiwNCiAgICAgICAgICAgICAgc2NhbGU9Yyg0LDAuNSksDQogICAgICAgICAgICAgIG1heC53b3Jkcz1pbnB1dCRud29yZHMsDQogICAgICAgICAgICAgIG1pbi5mcmVxPTEsDQogICAgICAgICAgICAgIHJhbmRvbS5vcmRlcj1GQUxTRSwNCiAgICAgICAgICAgICAgcm90LnBlcj0wLjMsDQogICAgICAgICAgICAgIHVzZS5yLmxheW91dD1ULA0KICAgICAgICAgICAgICByYW5kb20uY29sb3I9RkFMU0UsDQogICAgICAgICAgICAgIGNvbG9ycz1icmV3ZXIucGFsKDUsIkJsdWVzIiksIA0KICAgICAgICAgICAgbWFpbj1pbnB1dCRzcGVlY2gyKQ0KICAgICAgfSkNCiAgICAgIA0KICAgICAgb2JzZXJ2ZSh7DQogICAgICAgIGlmIChpbnB1dCRjbG9zZT4wKSBzdG9wQXBwKCkNCiMgICAgICAgIGpzJGNsb3NlV2luZG93KCkNCiAgICB9KQ0KICAgIH0sDQogICAgb3B0aW9ucyA9IGxpc3QoaGVpZ2h0ID0gNjAwKQ0KKQ0KDQpgYGANCg0KIyMjIyBTZW50aW1lbnRhbCBBbmFseXNpcyAgDQpGb3IgdGhlIGZpdmUgaW5hdWd1cmFsIGFkZHJlc3NlcyB3aGljaCBhcmUgY29uc2lkZXJlZCB0aGUgYmVzdCBhY3Jvc3Mgb2YgYWxsIHRpbWUsIHdlIGNhbiBzZWUgaG93IHRoZWlyIGVtb3Rpb24gY2hhbmdlZCBvdmVyIHRoZSBzcGVlY2gsIGFzIHNob3duIGluIHRoZSBwbG90IGJlbG93LiANCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEzLCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDIsMiwyLDEpLCBtZnJvdz1jKDcsMSkpDQpTZW50ZW5jZS5MZW5ndGguU2VudGltZXRhbC5QbG90KHNlbnRlbmNlLmxpc3QsIlRob21hc0plZmZlcnNvbiIsMSkNClNlbnRlbmNlLkxlbmd0aC5TZW50aW1ldGFsLlBsb3Qoc2VudGVuY2UubGlzdCwiQWJyYWhhbUxpbmNvbG4iLDIpDQpTZW50ZW5jZS5MZW5ndGguU2VudGltZXRhbC5QbG90KHNlbnRlbmNlLmxpc3QsIkZyYW5rbGluRFJvb3NldmVsdCIsMSkNClNlbnRlbmNlLkxlbmd0aC5TZW50aW1ldGFsLlBsb3Qoc2VudGVuY2UubGlzdCwiRnJhbmtsaW5EUm9vc2V2ZWx0IiwyKQ0KU2VudGVuY2UuTGVuZ3RoLlNlbnRpbWV0YWwuUGxvdChzZW50ZW5jZS5saXN0LCJKb2huRktlbm5lZHkiLDEpDQpgYGANCg0KRm9yIHRoZSBtb3N0IHJlY2VudCBmb3VyIFBPVFVTLCB3aG8gbWF5IHNvdW5kIG1vcmUgZmFtaWxpYXIgdGhhbiBvdGhlcnMgdG8gb3VyIE1pbGxlbm5pYWxzLCBsZXQncyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgaG93IHRoZWlyIGVtb3Rpb24gY2hhbmdlZCBhY3Jvc3Mgb3ZlciB0aGUgc3BlZWNoZXMuIEJlbG93IHNob3dzIHRoZSBhbmFseXNpcyBvZiBzcGVlY2hlcyBmcm9tIGJvdGggdGhlIGZpcnN0IGFuZCBzZWNvbmQgdGVybXMsIHdoaWxlIHRvIG5vdGUgdGhhdCBQcmVzaWRlbnQgVHJ1bXAgaXMgY3VycmVudGx5IGluIGhpcyBmaXJzdCB0ZXJtLiAgIA0KYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoMiwyLDIsMSksIG1mcm93PWMoNywxKSkNCg0KU2VudGVuY2UuTGVuZ3RoLlNlbnRpbWV0YWwuUGxvdChzZW50ZW5jZS5saXN0LCJXaWxsaWFtSkNsaW50b24iLDEpDQpTZW50ZW5jZS5MZW5ndGguU2VudGltZXRhbC5QbG90KHNlbnRlbmNlLmxpc3QsIldpbGxpYW1KQ2xpbnRvbiIsMikNClNlbnRlbmNlLkxlbmd0aC5TZW50aW1ldGFsLlBsb3Qoc2VudGVuY2UubGlzdCwiR2VvcmdlV0J1c2giLDEpDQpTZW50ZW5jZS5MZW5ndGguU2VudGltZXRhbC5QbG90KHNlbnRlbmNlLmxpc3QsIkdlb3JnZVdCdXNoIiwyKQ0KU2VudGVuY2UuTGVuZ3RoLlNlbnRpbWV0YWwuUGxvdChzZW50ZW5jZS5saXN0LCJCYXJhY2tPYmFtYSIsMSkNClNlbnRlbmNlLkxlbmd0aC5TZW50aW1ldGFsLlBsb3Qoc2VudGVuY2UubGlzdCwiQmFyYWNrT2JhbWEiLDIpDQpTZW50ZW5jZS5MZW5ndGguU2VudGltZXRhbC5QbG90KHNlbnRlbmNlLmxpc3QsIkRvbmFsZEpUcnVtcCIsMSkNCmBgYA0KDQojIyMjIFRvcGljIE1vZGVsaW5nIA0KYGBge3J9DQojIFJ1biBMREEgKExhdGVudCBEaXJpY2hsZXQgYWxsb2NhdGlvbikNCg0KIyBTZXQgcGFyYW1ldGVycyBmb3IgR2liYnMgc2FtcGxpbmcNCmJ1cm5pbiA8LSA0MDAwICMgRHJvcCB0aGUgZmlyc3QgNDAwMCBzYW1wbGVzDQppdGVyIDwtIDIwMDANCnRoaW4gPC0gNTAwICMgUGljayBvbmx5IGV2ZXJ5IDUwMCBndWVzc2VzDQpzZWVkIDwtbGlzdCgyMDAzLDUsNjMsMTAwMDAxLDc2NSkNCm5zdGFydCA8LSA1DQpiZXN0IDwtIFRSVUUgIyBTYW1wbGUgd2l0aCB0aGUgbGFyZ2VzdCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIA0KayA8LSA4ICMgTnVtYmVyIG9mIHRvcGljcw0KDQojIFJ1biBMREEgdXNpbmcgR2liYnMgc2FtcGxpbmcNCmxkYU91dC5hbGwgPC1MREEoZHRtLmFsbC5sZGEsIGssIG1ldGhvZD0iR2liYnMiLCBjb250cm9sPWxpc3QobnN0YXJ0PW5zdGFydCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IHNlZWQsIGJlc3Q9YmVzdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBidXJuaW4gPSBidXJuaW4sIGl0ZXIgPSBpdGVyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGluPXRoaW4pKQ0KIyBXcml0ZSBvdXQgcmVzdWx0cw0KIyBEb2NzIHRvIHRvcGljcw0KbGRhT3V0LnRvcGljcy5hbGwgPC0gYXMubWF0cml4KHRvcGljcyhsZGFPdXQuYWxsKSkNCnRhYmxlKGMoMTprLCBsZGFPdXQudG9waWNzLmFsbCkpDQp3cml0ZS5jc3YobGRhT3V0LnRvcGljcy5hbGwsZmlsZT1wYXN0ZSgiLi4vb3V0cHV0L0xEQUdpYmJzICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGssIiBEb2NzVG9Ub3BpY3MuY3N2Iiwgc2VwID0gJycpKQ0KDQojIFRvcCAxMCB0ZXJtcyBpbiBlYWNoIHRvcGljDQpsZGFPdXQudGVybXMuYWxsIDwtIGFzLm1hdHJpeCh0ZXJtcyhsZGFPdXQuYWxsLDEwKSkNCndyaXRlLmNzdihsZGFPdXQudGVybXMuYWxsLGZpbGU9cGFzdGUoIi4uL291dHB1dC9MREFHaWJicyAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGssIiBUb3BpY3NUb1Rlcm1zLmNzdiIsIHNlcCA9ICcnKSkNCg0KIyBQcm9iYWJpbGl0aWVzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIHRvcGljIGFzc2lnbm1lbnQNCnRvcGljUHJvYmFiaWxpdGllcy5hbGwgPC0gYXMuZGF0YS5mcmFtZShsZGFPdXQuYWxsQGdhbW1hKQ0Kd3JpdGUuY3N2KHRvcGljUHJvYmFiaWxpdGllcy5hbGwsZmlsZT1wYXN0ZSgiLi4vb3V0cHV0L0xEQUdpYmJzICIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaywiIFRvcGljUHJvYmFiaWxpdGllcy5jc3YiLCBzZXAgPSAnJykpDQoNCnRlcm1zLmJldGEgPC0gbGRhT3V0LmFsbEBiZXRhDQp0ZXJtcy5iZXRhIDwtIHNjYWxlKHRlcm1zLmJldGEpDQp0b3BpY3MudGVybXMgPC0gTlVMTA0KZm9yKGkgaW4gMTprKXsNCiAgdG9waWNzLnRlcm1zLmFsbCA8LSByYmluZCh0b3BpY3MudGVybXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgbGRhT3V0LmFsbEB0ZXJtc1tvcmRlcih0ZXJtcy5iZXRhW2ksXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdXSkNCn0NCiN0b3BpY3MudGVybXMuYWxsDQpsZGFPdXQudGVybXMuYWxsWzE6MyxdDQpgYGANCg0KQmFzZWQgb24gdGhlIG1vc3QgcG9wdWxhciB0ZXJtcyBhbmQgdGhlIG1vc3Qgc2FsaWVudCB0ZXJtcyBmb3IgZWFjaCB0b3BpYywgd2UgYXNzaWduIGEgaGFzaHRhZyB0byBlYWNoIHRvcGljLCB3aGljaCBhcmUgIkdvdmVybWVudCIsICJXYXIiLCAiV29ybGQiLCAiTGF3IiwgIlRpbWUiLCAiTmF0aW9uIiwgIlBlb3BsZSIsIGFyZSAiQ291bnRyeSIuIA0KYGBge3J9DQp0b3BpY3MuaGFzaCA8LSBjKCJHb3Zlcm1lbnQiLCAiV2FyIiwgIldvcmxkIiwgIkxhdyIsIA0KICAgICAgICAgICAgICAgICAiVGltZSIsICJOYXRpb24iLCAiUGVvcGxlIiwgIkNvdW50cnkiKQ0KcHJpbnQodG9waWNzLmhhc2gpDQpjb3JwdXMubGlzdCRsZGF0b3BpYyA8LSBhcy52ZWN0b3IobGRhT3V0LnRvcGljcy5hbGwpDQpjb3JwdXMubGlzdCRsZGFoYXNoIDwtIHRvcGljcy5oYXNoW2xkYU91dC50b3BpY3MuYWxsXQ0KDQpjb2xuYW1lcyh0b3BpY1Byb2JhYmlsaXRpZXMuYWxsKSA8LSB0b3BpY3MuaGFzaA0KY29ycHVzLmxpc3QuZGYgPC0gY2JpbmQoY29ycHVzLmxpc3QsIHRvcGljUHJvYmFiaWxpdGllcy5hbGwpDQpgYGANCldlIGNhbiB1c2UgV29yZCBDbG91ZCB0byB2aXN1YWxpemUgIHRoZSB0b3AgdG9waWNzIGFzIHNob3duIGJlbG93LiANCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD00LjV9DQpzZXQuc2VlZCgxMjMpDQp3b3JkY2xvdWQoY29ycHVzLmxpc3QuZGYkbGRhaGFzaCwNCiAgICAgICAgICBzY2FsZT1jKDUsMC41KSwNCiAgICAgICAgICBtYXgud29yZHM9MTAwLA0KICAgICAgICAgIG1pbi5mcmVxPTEsDQogICAgICAgICAgcmFuZG9tLm9yZGVyPUZBTFNFLA0KICAgICAgICAgIHJvdC5wZXI9MC4zLA0KICAgICAgICAgIHVzZS5yLmxheW91dD1ULA0KICAgICAgICAgIHJhbmRvbS5jb2xvcj1GQUxTRSwNCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg1LCJCbHVlcyIpDQogICAgICAgICAgKQ0KYGBgDQojIyMgTnVtYmVyIG9mIFRlcm1zIFNlcnZlZCAgPGEgbmFtZT0iVGVybXNTZXJ2ZWQiPjwvYT4gIHsudGFic2V0fSAgDQojIyMjIE92ZXJ2aWV3ICANCioqU3VtbWFyeSBvZiBHcm91cCBvZiBJbnRlcmVzdCoqDQoNCnwgTnVtYmVyIG9mIFRlcm1zIFNlcnZlZCB8IE51bWJlciBvZiBQcmVzaWRlbnRzIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tOnwgICAgIA0KfCBPbmUgVGVybSAgICAgICAgICAgICAgIHwgMjIgICAgICAgICAgICAgICAgICAgfCANCnwgVHdvIFRlcm1zICAgICAgICAgICAgICB8IDE2ICAgICAgICAgICAgICAgICAgIHwgIA0KfCBGb3VyIFRlcm1zICAgICAgICAgICAgIHwgIDEgICAgICAgICAgICAgICAgICAgfCANCg0KVGhpcyBhbmFseXNpcyBoYXMgcHJpbWFyaWx5IGZvY3VzZWQgb24gcHJlc2lkZW50cyB3aG8gc2VydmVkIG9uZSBvciB0d28gdGVybXMuIFRoZSBhbmFseXNpcyBvZiB0aGUgc3BlZWNoZXMgb2YgRnJhbmtsaW4gUm9vc2V2ZWx0LCB3aG8gd2FzIHRoZSBvbmx5IFBPVFVTIHdobyBzZXJ2ZWQgbW9yZSB0aGFuIHR3byB0ZXJtcywgbWF5IGJlIGluY29ycG9yYXRlZCBpbiB0aGUgZnVydHVyZS4gDQoNCiMjIyMgU3RhZ2luZyAgDQpgYGB7cn0NCiMjIyBOdW1iZXIgb2YgVGVybXMgU2VydmVkDQojIFN1bW1hcnkgb2YgZ3JvdXBzIGJ5IHRoZSBudW1iZXIgb2YgdGVybXMgc2VydmVkDQojaW5hdWcubGlzdCU+JQ0KIyAgZ3JvdXBfYnkoVGVybSklPiUNCiMgIHN1bW1hcmlzZShDb3VudCA9IG5fZGlzdGluY3QoUHJlc2lkZW50KSkNCg0KIyBJZGVudGlmeSAnRmlsZScgZm9yIGVhY2ggZ3JvdXAgDQpmb3VyLnRlcm1zIDwtIHVuaXF1ZShpbmF1Zy5saXN0JEZpbGVbaW5hdWcubGlzdCRUZXJtPjJdKQ0KdHdvcGx1cy50ZXJtcyA8LSBpbmF1Zy5saXN0JEZpbGVbaW5hdWcubGlzdCRUZXJtPT0yXQ0KdHdvLnRlcm1zIDwtIHR3b3BsdXMudGVybXNbdHdvcGx1cy50ZXJtcyAhPSBmb3VyLnRlcm1zXQ0Kb25lLnRlcm0gPC0gaW5hdWcubGlzdCRGaWxlWyEoaW5hdWcubGlzdCRGaWxlJWluJXR3b3BsdXMudGVybXMpXQ0KDQojIENyZWF0ZSBncm91cCBhbmQgZ3JvdXAgbmFtZQ0KVGVybUdyb3VwIDwtIGxpc3QoIk9uZSBUZXJtIj1vbmUudGVybSwgIlR3byBUZXJtcyI9dHdvLnRlcm1zLCAiRm91ciBUZXJtcyI9Zm91ci50ZXJtcykNCiNUZXJtR3JvdXAubmFtZSA8LSAiTnVtYmVyIG9mIFRlcm1zIFNlcnZlZCINCmBgYA0KUE9UVVMgYXJlIGFzc2lnbmVkIGludG8gdGhyZWUgZ3JvdXBzIGJ5IHRoZSBudW1iZXIgb2YgdGVybXMgdGhleSBoYXZlIHNlcnZlZC4gDQoNCiogIGByIGxlbmd0aChvbmUudGVybSlgIFBPVFVTIHdobyBoYXZlIHNlcnZlZCBvbmx5IG9uZSB0ZXJtOg0KYGBge3J9DQpUZXJtR3JvdXAkYE9uZSBUZXJtYA0KYGBgDQoqICAgYHIgbGVuZ3RoKHR3by50ZXJtcylgIFBPVFVTIHdobyBoYXZlIHNlcnZlZCB0d28gdGVybXM6IA0KYGBge3J9DQpUZXJtR3JvdXAkYFR3byBUZXJtc2ANCmBgYA0KKiAgIGByIGxlbmd0aChmb3VyLnRlcm1zKWAgUE9UVVMgd2hvIGhhcyBzZXJ2ZWQgbW9yZSB0aGFuIHR3byB0ZXJtczogDQpgYGB7cn0NClRlcm1Hcm91cCRgRm91ciBUZXJtc2ANCmBgYA0KDQojIyMjIExlbmd0aCBvZiBTcGVlY2hlcyAgDQpCeSBsb29raW5nIHRoZSBiYXIgcGxvdHMgYW5kIGJveC13aGlza2VyIHBsb3RzIG9mIHRoZSBudW1iZXIgb2Ygd29yZHMgdXNlZCBpbiBhIHNwZWVjaCwgd2UgY2FuIHNlZSB0aGF0IHRoZSBsZW5ndGggb2YgdGhlIGluYXVndXJhbCBzcGVlY2hlcyBvZiB0aGUgUE9UVVMgd2hvIHNlcnZlZCBvbmx5IG9uZSB0ZXJtIGlzIGFwcGFyZW50bHkgbG9uZ2VyIHRoYW4gdGhhdCBvZiB0aGUgb3RoZXIgUE9UVVMgd2hvIHNlcnZlZCBtb3JlIHRoYW4gb25lIHRlcm0uICAgDQpgYGB7ciwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA1LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSwzLDIsMyksIG1mcm93PWMoMSwyKSkNCg0KIyBDcmVhdGUgYmFyIHBsb3Qgb2YgZWFjaCBzcGVlY2gNClNwZWVjaC5MZW5ndGguQmFyLlBsb3QoaW5hdWcubGlzdCxUZXJtR3JvdXApDQojIENyZWF0ZSBib3ggcGxvdCBvZiBlYWNoIHNwZWVjaCBieSBncm91cCANClNwZWVjaC5MZW5ndGguQm94LlBsb3QoaW5hdWcubGlzdCxUZXJtR3JvdXApDQpgYGANCkZvciBQT1RVUyB3aG8gc2VydmVkIHR3byB0ZXJtcywgd2UgY29tcGFyZWQgdGhlIGxlbmd0aCBvZiBzcGVlY2hlcyBvZiB0aGVpciBmaXJzdCB0ZXJtIGFuZCBzZW5jb25kIHRlcm0uIEFzIHNob3duIGluIHRoZSBwbG90cywgdGhlIGxlbmd0aCBvZiBzcGVlY2hlcyBvZiB0aGVpciB0d28gdGVybXMgYXJlIGdlbmVyYWxseSBjb25zaXN0ZW50LCBleGNlcHQgZm9yIHRoYXQgb2YgQWJyYWhhbSBMaW5jb2xuJ3MgLSBBYnJhaGFtIExpbmNvbG4ncyBzZW5jb25kIHRlcm0gc3BlZXBjaCBpcyBhIGxvdCBzaG9ydGVyIHRoYW4gaGlzIGZpcnN0LiAgIA0KYGBge3IsIGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsMywyLDMpLCBtZnJvdz1jKDEsMikpDQoNCiMgQ3JlYXRlIGJhciBwbG90IG9mIGVhY2ggc3BlZWNoDQpTcGVlY2guTGVuZ3RoLkJhci5QbG90KGluYXVnLmxpc3QsVGVybUdyb3VwWzJdLA0KICAgICAgICAgICAgICAgICAgICAgICBJbi5UZXJtID1saXN0KCJGaXJzdCBUZXJtIj0iMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNlY29uZCBUZXJtIj0iMiIpLA0KICAgICAgICAgICAgICAgICAgICAgICBJbi5ieS5UZXJtID0gVFJVRSkNCiMgQ3JlYXRlIGJveCBwbG90IG9mIGVhY2ggc3BlZWNoIGJ5IGdyb3VwIA0KU3BlZWNoLkxlbmd0aC5Cb3guUGxvdChpbmF1Zy5saXN0LFRlcm1Hcm91cFsyXSwNCiAgICAgICAgICAgICAgICAgICAgICAgSW4uVGVybSA9IGxpc3QoIkZpcnN0IFRlcm0iPSIxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2Vjb25kIFRlcm0iPSIyIiksDQogICAgICAgICAgICAgICAgICAgICAgIEluLmJ5LlRlcm0gPSBUUlVFKQ0KYGBgDQoNCiMjIyMgTGVuZ3RoIG9mIFNlbnRlbmNlcyANCkJ5IGxvb2tpbmcgdGhlIGJlZSBzd2FybSBwbG90cyBhbmQgYm94LXdoaXNrZXIgcGxvdHMgb2YgdGhlIG51bWJlciBvZiB3b3JkcyB1c2VkIGluIGEgc2VudGVuY2UsIHdlIGNhbiBzZWUgdGhhdCB0aGUgbGVuZ3RoIG9mIHNlbnRlbmNlcyB1c2VkIGJ5IFBPVFVTIHdobyBzZXJ2ZWQgb25seSBvbmUgdGVybSBpcyBnZW5lcmFsbHkgYSBiaXQgbG9uZ2VyIHRoYW4gdGhvc2Ugb2Ygb3RoZXIgUE9UVVMgd2hvIHNlcnZlZCBtb3JlIHRoYW4gb25lIHRlcm0uICAgDQpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDUuNSwyLDEpLCBtZnJvdz1jKDEsMikpDQoNCkJlZS5Td2FybS5QbG90KHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwKQ0KQmVlLlN3YXJtLlBsb3QuR3JvdXAoc2VudGVuY2UubGlzdCxUZXJtR3JvdXApDQpgYGANCkJlbG93IGxpc3RlZCB0aGUgc2hvcnRlc3QgYW5kIGxvbmdlc3Qgc2VudGVuc2VzIHVzZWQgaW4gdGhlc2Ugc3BlZWNoZXMgZm9yIGVhY2ggZ291cC4gIA0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIH0NClNlbnRlbmNlLlNlYXJjaGVyLkdyb3VwKHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgSW4uVGVybT1jKCIxIiwiMiIsIjMiLCI0IiksSW4uTG9uZz1GQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgICBJbi5taW4ud29yZHM9MywgSW4ubi5zZW50ZW5jZXM9NSkNClNlbnRlbmNlLlNlYXJjaGVyLkdyb3VwKHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgSW4uVGVybT1jKCIxIiwiMiIsIjMiLCI0IiksSW4uTG9uZz1UUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgIEluLm1pbi53b3Jkcz0zLCBJbi5uLnNlbnRlbmNlcz01KQ0KYGBgDQpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDUuNSwyLDEpLCBtZnJvdz1jKDEsMikpDQojIENyZWF0ZSBiZWUgc3dhcm0gcGxvdCBmb3IgJ1R3byBUZXJtcycgZ3JvdXANCg0KaWkudmFyIDwtIHNlbnRlbmNlLmxpc3QlPiUNCiAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChUZXJtR3JvdXBbMl0pLA0KICAgICAgICAgICAgICAgICAgIFRlcm0laW4lYygxLDIpKQ0KaWkudmFyJEZpbGUgPC0gZmFjdG9yKGlpLnZhciRGaWxlKQ0KaWkudmFyJEZpbGVPcmRlcmVkIDwtIHJlb3JkZXIoaWkudmFyJEZpbGUsIGlpLnZhciR3b3JkLmNvdW50LCBtZWFuLCBvcmRlcj1UKQ0KICANCmJlZXN3YXJtKHdvcmQuY291bnR+RmlsZU9yZGVyZWQsIA0KICAgICAgICAgICAgIGRhdGEgPSBpaS52YXIlPiVmaWx0ZXIoVGVybT09MSksDQogICAgICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsDQogICAgICAgICAgICAgcGNoPTE2LCBjb2w9YWxwaGEoYnJld2VyLnBhbCg5LCAiQmx1ZXMiKVs0XSwgMSksIA0KICAgICAgICAgICAgIGNleD0wLjU1LCBjZXguYXhpcz0wLjU1LCBjZXgubGFiPTEsDQogICAgICAgICAgICAgc3BhY2luZz01L25sZXZlbHMoaWkudmFyJEZpbGVPcmRlcmVkKSwNCiAgICAgICAgICAgICBsYXM9MiwgeWxhYj0iIiwgeGxhYj0iIiwNCiAgICAgICAgICAgICBtYWluPSJOdW1iZXIgb2YgV29yZHMgaW4gYSBTZW50ZW5jZSIpDQpiZWVzd2FybSh3b3JkLmNvdW50fkZpbGVPcmRlcmVkLCANCiAgICAgICAgICAgICBkYXRhID0gaWkudmFyJT4lZmlsdGVyKFRlcm09PTIpLA0KICAgICAgICAgICAgIGFkZD1UUlVFLA0KICAgICAgICAgICAgIGhvcml6b250YWwgPSBUUlVFLA0KICAgICAgICAgICAgIHBjaD0xNiwgY29sPWFscGhhKGJyZXdlci5wYWwoOSwgIkJsdWVzIilbN10sIDEpLCANCiAgICAgICAgICAgICBjZXg9MC41NSwgY2V4LmF4aXM9MC41NSwgY2V4LmxhYj0xLA0KICAgICAgICAgICAgIHNwYWNpbmc9NS9ubGV2ZWxzKGlpLnZhciRGaWxlT3JkZXJlZCkpDQoNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBpbnNldD0gMC4wMiwgbGVnZW5kID1jKCJGaXJzdCBUZXJtIiwgIlNlY29uZCBUZXJtIiksIA0KICAgICAgICAgdGV4dC5jb2wgPSBicmV3ZXIucGFsKDksIkJsdWVzIilbYyg0LDcpXSwgDQogICAgICAgICBjZXggPSAwLjcsIGJveC5sdHk9MiApDQoNCmJlZXN3YXJtKHdvcmQuY291bnR+VGVybSwgDQogICAgICAgICAgICAgZGF0YSA9IGlpLnZhciwNCiAgICAgICAgICAgICBob3Jpem9udGFsID0gVFJVRSwNCiAgICAgICAgICAgICBwY2g9MTYsIGNvbD1hbHBoYShicmV3ZXIucGFsKDksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmx1ZXMiKVtjKDQsNyldLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjQpLCANCiAgICAgICAgICAgICBjZXg9MC41NSwgY2V4LmF4aXM9MC41NSwgY2V4LmxhYj0xLA0KICAgICAgICAgICAgIHNwYWNpbmc9NS9ubGV2ZWxzKGlpLnZhciRGaWxlT3JkZXJlZCksDQogICAgICAgICAgICAgbGFzPTIsIHlsYWI9IiIsIHhsYWI9IiIsDQogICAgICAgICAgICAgbWFpbj0iTnVtYmVyIG9mIFdvcmRzIGluIGEgU2VudGVuY2UiKQ0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgaW5zZXQ9MC4wMiwgbGVnZW5kID1jKCJGaXJzdCBUZXJtIiwgIlNlY29uZCBUZXJtIikgLCANCiAgICAgICAgIHRleHQuY29sID0gYnJld2VyLnBhbCg5LCJCbHVlcyIpW2MoNCw3KV0sIA0KICAgICAgICAgY2V4ID0gMC43LCBib3gubHR5PTIgKQ0KICBib3hwbG90KGlpLnZhciR3b3JkLmNvdW50fmlpLnZhciRUZXJtLCBob3Jpem9udGFsPVRSVUUsIA0KICAgICAgICAgIGNvbD0iIzAwMDBmZjIyIiwgYXhlcz1GQUxTRSwgYWRkPVRSVUUpDQpgYGANCkZvciBQT1RVUyB3aG8gc2VydmVkIHR3byB0ZXJtcywgd2UgY29tcGFyZWQgc3BlZWNoZXMgb2YgdGhlaXIgZmlyc3QgdGVybSBhbmQgc2VuY29uZCB0ZXJtLiBGcm9tIHRoZSBwbG90cywgd2UgY2FuIHNlZSB0aGF0IHNwZWVjaGVzIG9mIHRoZWlyIHR3byB0ZXJtcyBhcmUgZ2VuZXJhbGx5IGNvbnNpc3RlbnQuIEJlbG93IGxpc3RlZCB0aGUgc2hvcnRlc3QgYW5kIGxvbmdlc3Qgc2VudGVuc2VzIGluIHRoZSBzcGVlY2hlcyBvZiB0aGUgcHJlc2lkZW50cyB3aG8gc2VydmVkIHR3byB0ZXJtcy4gDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCnNob3J0LmZpcnN0IDwtIFNlbnRlbmNlLlNlYXJjaGVyLkdyb3VwKHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwLGMoIjEiKSxGQUxTRSlbLDJdDQpzaG9ydC5zZWNvbmQgPC0gU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAoc2VudGVuY2UubGlzdCxUZXJtR3JvdXAsYygiMiIpLEZBTFNFKVssMl0NCnNob3J0LnR3by50ZXJtcyA8LSBkYXRhLmZyYW1lKHNob3J0LmZpcnN0LCBzaG9ydC5zZWNvbmQpDQpjb2xuYW1lcyhzaG9ydC50d28udGVybXMpIDwtIGMoIkZpcnN0IFRlcm0iLCAiU2Vjb25kIFRlcm0iKQ0Kc2hvcnQudHdvLnRlcm1zDQoNCmxvbmcuZmlyc3QgPC0gU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAoc2VudGVuY2UubGlzdCxUZXJtR3JvdXAsYygiMSIpLFRSVUUpWywyXQ0KbG9uZy5zZWNvbmQgPC0gU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAoc2VudGVuY2UubGlzdCxUZXJtR3JvdXAsYygiMiIpLFRSVUUpWywyXQ0KbG9uZy50d28udGVybXMgPC0gZGF0YS5mcmFtZShsb25nLmZpcnN0LCBsb25nLnNlY29uZCkNCmNvbG5hbWVzKGxvbmcudHdvLnRlcm1zKSA8LSBjKCJGaXJzdCBUZXJtIiwgIlNlY29uZCBUZXJtIikNCmxvbmcudHdvLnRlcm1zDQpgYGANCg0KIyMjIyBXb3JkIENsb3VkICANClRoZSB0ZXh0IHdpdGggbGFyZ2VyIGluIHNpemUgYW5kIGRhcmtlciBpbiBjb2xvciBhcyBzaG93biBpbiB0aGUgd29yZHMgY2xvdWQgYmVsb3cgYXJlIHRoZSB3b3JkcyB0aGF0IGFwcGVhcmVkIG1vcmUgb2Z0ZW4gaW4gdGhlIHNwZWVjaGVzLiBBcyB3ZSBjYW4gc2VlLCB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyB1c2VkIGJ5IHRoZSBQT1RVUyBvZiB0aGVzZSB0aHJlZSBncm91cHMgYXJlIHRoZSBzYW1lLCB3aGljaCBpbmNsdWUgIndpbGwiLCAicGVvcGxlIiwgYW5kICJnb3Zlcm5tZW50Ii4gDQpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoMSwxLDEsMSksIG1mcm93PWMoMSxsZW5ndGgoVGVybUdyb3VwKSkpDQpzZXQuc2VlZCgxMjMpDQpXb3JkLkNsb3VkLkdyb3VwKGluYXVnLmxpc3QsVGVybUdyb3VwKQ0KYGBgDQpGb3IgUE9UVVMgd2hvIHNlcnZlZCB0d28gdGVybXMsIGFzIHNob3duIHRoZSBpbiB0aGUgd29yZCBjbG91ZCBiZWxvdywgdGhlIG1vc3QgZnJlcXVlbnRseSB1c2VkIHdvcmRzIGFyZSB2ZXJ5IHNpbWlsYXIgYmV0d2VlbiB0aGVpciBmaXJzdCB0ZXJtIGFuZCBzZW5jb25kIHRlcm0uICANCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDEsMSwxLDEpLCBtZnJvdz1jKDEsMikpDQpzZXQuc2VlZCgxMjMpDQpmb3IgKGkgaW4gMToyKXsNCiAgaWkudmFyIDwtIGluYXVnLmxpc3QlPiUNCiAgICAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoVGVybUdyb3VwWzJdKSkNCg0KICBpaS5kb2NzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UoaWkudmFyJEZ1bGx0ZXh0KSkNCiAgaWkuZG9jcyA8LSB0bV9tYXAoaWkuZG9jcywgc3RyaXBXaGl0ZXNwYWNlKSAjIEVsaW1pbmF0ZSBleHRyYSB3aGl0ZXNwYWNlDQogIGlpLmRvY3MgPC0gdG1fbWFwKGlpLmRvY3MsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpICMgQ29udmVydCB0byBsb3dlciBjYXNlDQogIGlpLmRvY3MgPC0gdG1fbWFwKGlpLmRvY3MsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkgIyBSZW1vdmUgc3RvcHdvcmRzDQogIGlpLmRvY3MgPC0gdG1fbWFwKGlpLmRvY3MsIHJlbW92ZVdvcmRzLCBjaGFyYWN0ZXIoMCkpDQogIGlpLmRvY3MgPC0gdG1fbWFwKGlpLmRvY3MsIHJlbW92ZVB1bmN0dWF0aW9uKSAjIFJlbW92ZSBwdW5jdHVhdGlvbnMNCiAgI2lpLmRvY3MgPC0gdG1fbWFwKGlpLmRvY3MsIHJlbW92ZU51bWJlcnMpICMgUmVtb3ZlIG51bWJlcnMNCiAgI2lpLmRvY3MgPC0gdG1fbWFwKGlpLmRvY3MsIHN0ZW1Eb2N1bWVudCkgIyBTdGVtIGRvY3VtZW50DQogICAgICANCiAgaWkudGRtIDwtIFRlcm1Eb2N1bWVudE1hdHJpeChpaS5kb2NzKQ0KICBpaS50ZG0udGlkeSA8LSB0aWR5KGlpLnRkbSkNCiAgaWkudGRtLnZhciA8LSBzdW1tYXJpc2UoZ3JvdXBfYnkoaWkudGRtLnRpZHksIHRlcm0pLCBzdW0oY291bnQpKQ0KICAgICAgDQoNCiAgd29yZGNsb3VkKGlpLnRkbS52YXIkdGVybSwgaWkudGRtLnZhciRgc3VtKGNvdW50KWAsDQogICAgICAgICAgICAgICAgc2NhbGU9Yyg1LDAuNSksDQogICAgICAgICAgICAgICAgbWF4LndvcmRzPTEwMCwNCiAgICAgICAgICAgICAgICBtaW4uZnJlcT0xLA0KICAgICAgICAgICAgICAgIHJhbmRvbS5vcmRlcj1GQUxTRSwNCiAgICAgICAgICAgICAgICByb3QucGVyPTAuMywNCiAgICAgICAgICAgICAgICB1c2Uuci5sYXlvdXQ9VCwNCiAgICAgICAgICAgICAgICByYW5kb20uY29sb3I9RkFMU0UsDQogICAgICAgICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoNSwgIkJsdWVzIikNCiAgICAgICAgICApDQogIHRpdGxlKG1haW4gPSBjKCJGaXJzdCBUZXJtIiwiU2Vjb25kIFRlcm0iKVtpXSwgDQogICAgICAgICAgICBjZXgubWFpbj0xLjUsDQogICAgICAgICAgICBjb2wubWFpbj1icmV3ZXIucGFsKDUsICJCbHVlcyIpWzRdKQ0KfQ0KYGBgDQoNCiMjIyMgU2VudGltZW50YWwgQW5hbHlzaXMgIA0KQXMgc2hvd24gaW4gdGhlIGJhciBwbG90cyBiZWxvdywgdGhlIG92ZXJhbGwgZW1vdGlvbiBwYXR0ZXJuIGFyZSB2ZXJ5IHNpbWlsYXIgZm9yIHRoZXNlIHRocmVlIGdyb3Vwcy4gICAgDQpgYGB7ciwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDQuNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsNS41LDIsMSksIG1mcm93PWMobGVuZ3RoKFRlcm1Hcm91cCksMSkpDQojIENyZWF0ZSBiYXJwbG90IG9mIGF2ZXJhZ2UgdmFsdWUgb2YgZW1vdGlvbnMgZm9yIGVhY2ggZ3JvdXANClNlbnRpbWVudGFsLkFuYWx5c2lzLlBsb3RzKHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwKQ0KYGBgDQpXZSBhbHNvIGNvbXBhcmVkIHRoZSBmaXJzdCB0ZXJtIGFuZCBzZWNvbmQgdGVybSBzcGVlY2hlcyBvZiB0aGUgUE9UVVMgd2hvIHNlcnZlZCB0d28gdGVybXMuIEFzIHNob3duIGJlbG93LCB0aGUgZmVhciBsZXZlbCBpbmNyZWFzZXMgYnV0IHRoZSBzYWRuZXNzIGxldmVsIGRlY3JlYXNlcyBmcm9tIHRoZSBmaXJzdCB0ZXJtIHRvIHRoZSBzZWNvbmQgdGVybSwgd2hpbGUgYWxsIG90aGVyIGVtb3Rpb25zIHNlZW1lZCBhdCB0aGUgc2FtZSBsZXZlbCBhcyB0aGUgdGhlIGZpcnN0IHRlcm0uICANCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygyLDEpKQ0KIyBDcmVhdCBIZWF0bWFwIGZvciBjb3JyZWxhdGlvbnMgYW5kIA0KIyBCYXJwbG90IGZvciBhdmVyYWdlIHZhbHVlIG9mIGVtb3Rpb25zIGZvciBncm91cHMgaW4gSW4uR3JvdXANCg0KIyAgaGVhdG1hcC4yKGNvcihzZW50ZW5jZS5saXN0JT4lDQojICAgICAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChUZXJtR3JvdXBbMl0pKSU+JQ0KIyAgICAgICAgICAgICAgICBzZWxlY3QoYW5nZXI6dHJ1c3QpKSwgDQojICAgICAgICAgIHNjYWxlID0gIm5vbmUiLCANCiMgICAgICAgICAgY29sID0gYmx1ZXJlZCgxMDApLCBtYXJnaW49Yyg2LCA2KSwga2V5PUYsDQojICAgICAgICAgIHRyYWNlID0gIm5vbmUiLCBkZW5zaXR5LmluZm8gPSAibm9uZSIpDQoNCiAgaS5lbW8ubWVhbnMudmFyIDwtIGNvbE1lYW5zKHNlbnRlbmNlLmxpc3QlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoVGVybUdyb3VwWzJdKSxUZXJtPT0xKSU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChhbmdlcjp0cnVzdCk+MC4wMSkNCiAgaWkuZW1vLm1lYW5zLnZhciA8LSBjb2xNZWFucyhzZW50ZW5jZS5saXN0JT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEZpbGUlaW4ldW5saXN0KFRlcm1Hcm91cFsyXSksVGVybT09MiklPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoYW5nZXI6dHJ1c3QpPjAuMDEpDQogIGJhcnBsb3QoaS5lbW8ubWVhbnMudmFyW29yZGVyKGkuZW1vLm1lYW5zLnZhcildLCANCiAgICAgICAgbGFzPTIsIGNvbD1icmV3ZXIucGFsKDgsIlBhc3RlbDIiKVtvcmRlcihpLmVtby5tZWFucy52YXIpXSwgDQogICAgICAgIGhvcml6PVQsIG1haW49IkZpcnN0IFRlcm0iLCB4bGFiID0gIkF2ZXJhZ2UgVmFsdWUgb2YgRW1vdGlvbnMiKQ0KICBiYXJwbG90KGlpLmVtby5tZWFucy52YXJbb3JkZXIoaWkuZW1vLm1lYW5zLnZhcildLCANCiAgICAgICAgbGFzPTIsIGNvbD1icmV3ZXIucGFsKDgsIlBhc3RlbDIiKVtvcmRlcihpaS5lbW8ubWVhbnMudmFyKV0sIA0KICAgICAgICBob3Jpej1ULCBtYWluPSJTZWNvbmQgVGVybSIsIHhsYWIgPSAiQXZlcmFnZSBWYWx1ZSBvZiBFbW90aW9ucyIpDQoNCmBgYA0KVHJ5aW5nIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBkaWZmZXJlbmNlLCB3ZSBoYXZlIGluZGVudGlmaWVkIHRoZSBtb3N0IGluZmx1ZW50aWFsIHNlbnRlbmNlcyB0aGF0IGRyb3ZlIHRoZSBmZWFyIGFuZCBzYWRuZXNzIGxldmVsIGZvciBib3RoIHRlcm1zLiAgIA0KYGBge3J9DQppLmVtby52YXIgPC0gc2VudGVuY2UubGlzdCU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChUZXJtR3JvdXBbMl0pLFRlcm09PTEpJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHNlbnRlbmNlcywgZmVhcixzYWRuZXNzKQ0KaWkuZW1vLnZhciA8LSBzZW50ZW5jZS5saXN0JT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEZpbGUlaW4ldW5saXN0KFRlcm1Hcm91cFsyXSksVGVybT09MiklPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc2VudGVuY2VzLCBmZWFyLHNhZG5lc3MpDQoNCmkuZmVhciA8LSBoZWFkKGkuZW1vLnZhciU+JWFycmFuZ2UoZGVzYyhmZWFyKSklPiVzZWxlY3Qoc2VudGVuY2VzKSkNCmlpLmZlYXIgPC0gaGVhZChpaS5lbW8udmFyJT4lYXJyYW5nZShkZXNjKGZlYXIpKSU+JXNlbGVjdChzZW50ZW5jZXMpKQ0KaS5zYWRuZXNzIDwtIGhlYWQoaS5lbW8udmFyJT4lYXJyYW5nZShkZXNjKHNhZG5lc3MpKSU+JXNlbGVjdChzZW50ZW5jZXMpKQ0KaWkuZGFkbmVzcyA8LSBoZWFkKGlpLmVtby52YXIlPiVhcnJhbmdlKGRlc2Moc2FkbmVzcykpJT4lc2VsZWN0KHNlbnRlbmNlcykpDQppLmVtby5kZiA8LSBjYmluZChpLmZlYXIsaWkuZmVhcixpLnNhZG5lc3MsaWkuZGFkbmVzcykNCmNvbG5hbWVzKGkuZW1vLmRmKSA8LSBjKCJGaXJzdCBUZXJtIEZlYXIiLCJTZWNvbmQgVGVybSBGZWFyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJGaXN0IFRlcm0gU2FkbmVzcyIsICJTZWNvbmQgVGVybSBTYWRuZXNzIikNCmkuZW1vLmRmDQpgYGANCg0KQmVsb3cgYXJlIHRoZSBlbW90aW9uYWxseSBjaGFyZ2VkIHNlbnRlbmNlcyBvZiBlYWNoIG9mIHRoZXNlIHRocmVlIGdyb3Vwcy4gIA0KYGBge3J9DQojIFdoYXQgYXJlIHRoZSBlbW90aW9uYWxseSBjaGFyZ2VkIHNlbnRlbmNlcz8gIA0KU2VudGltZW50YWwuU2VudGVuY2UuU2VhcmNoZXIoc2VudGVuY2UubGlzdCxUZXJtR3JvdXAsYygiMSIsIjIiLCIzIiwiNCIpKQ0KYGBgDQpGb3IgdGhlIFBPVFVTIHdobyBoYXZlIHNlcnZlZCB0d28gdGVybXMsIGJlbG93IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXMgb2YgdGhlaXIgZmlyc3QgdGVybSBzcGVlY2hlcy4NCmBgYHtyfQ0KU2VudGltZW50YWwuU2VudGVuY2UuU2VhcmNoZXIoc2VudGVuY2UubGlzdCxUZXJtR3JvdXBbMl0sYygiMSIpKQ0KYGBgDQpBbmQgYmVsb3cgbGlzdGVkIHRoZSBlbW90aW9uYWxseSBjaGFyZ2VkIHNlbnRlbmNlcyBvZiB0aGVpciBzZWNvbmQgdGVybSBzcGVlY2hlcy4gDQpgYGB7cn0NClNlbnRpbWVudGFsLlNlbnRlbmNlLlNlYXJjaGVyKHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwWzJdLGMoIjIiKSkNCmBgYA0KV2UgcGVyZm9ybSB0aGUgay1tZWFucyBjbHVzdGVyaW5nIGZvciB0aGUgdHdvIGdyb3VwcywgUE9UVVMgd2hvIHNlcnZlZCBvbmUgdGVybSBvciB0d28gdGVybXMuIFRoZSByZXN1bHRzIGNhbiBiZSB2aXN1YWxpemVkIGFzIHNob3duIGJlbG93LiAgDQpgYGB7ciwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDQuNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9YygxLDEsMSwxKSwgbWZyb3c9YygxLDIpKQ0KIyBPbmUgVGVybQ0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxUZXJtR3JvdXBbMV0sYygiMSIsIjIiLCIzIiwiNCIpKQ0KIyMgVmlzdWFsemUgY2x1c3RlcmluZyByZXN1bHRzIA0KZnZpel9jbHVzdGVyKGNsdXN0ZXJpbmcucnN0W1sxXV0sIA0KICAgICAgICAgICAgIHN0YW5kPUYsIHJlcGVsPSBUUlVFLA0KICAgICAgICAgICAgIGRhdGEgPSBjbHVzdGVyaW5nLnJzdFtbMl1dWywtMV0sIA0KICAgICAgICAgICAgIHhsYWI9IiIsIHhheHQ9Im4iLA0KICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudD1GQUxTRSkNCg0KIyBUd28gIFRlcm1zDQojIyBQZXJmb3JtIGstbWVhbnMgY2x1c3RlcmluZyANCmNsdXN0ZXJpbmcucnN0IDwtIFNlbnRpbWVudGFsLlNlbnRlbmNlLktNZWFucyhzZW50ZW5jZS5saXN0LFRlcm1Hcm91cFsyXSxjKCIxIiwiMiIsIjMiLCI0IikpDQoNCiMjIFZpc3VhbHplIGNsdXN0ZXJpbmcgcmVzdWx0cyANCmZ2aXpfY2x1c3RlcihjbHVzdGVyaW5nLnJzdFtbMV1dLCANCiAgICAgICAgICAgICBzdGFuZD1GLCByZXBlbD0gVFJVRSwNCiAgICAgICAgICAgICBkYXRhID0gY2x1c3RlcmluZy5yc3RbWzJdXVssLTFdLCANCiAgICAgICAgICAgICB4bGFiPSIiLCB4YXh0PSJuIiwNCiAgICAgICAgICAgICBzaG93LmNsdXN0LmNlbnQ9RkFMU0UpDQpgYGANCkZvciBQT1RVUyB3aG8gc2VydmVkIHR3byB0ZXJtcywgaXQgaXMgaW50ZXJlc3RpbmcgdG8gc2VlIHRoZXJlIGFyZSBhIGxvdCBkaWZmZXJlY2VzIGluIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMgYmV0d2VlbiB0aGVpciBmaXJzdCB0ZXJtIGFuZCBzZWNvbmQgdGVybSwgYXMgc2hvd24gYmVsb3cuIA0KYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDUuNSwyLDEpLCBtZnJvdz1jKDIsMSkpDQojIEZpcnN0IFRlcm0NCiMjIFBlcmZvcm0gay1tZWFucyBjbHVzdGVyaW5nIA0KY2x1c3RlcmluZy5yc3QgPC0gU2VudGltZW50YWwuU2VudGVuY2UuS01lYW5zKHNlbnRlbmNlLmxpc3QsVGVybUdyb3VwWzJdLGMoIjEiKSkNCiMjIFZpc3VhbHplIGNsdXN0ZXJpbmcgcmVzdWx0cyANCmZ2aXpfY2x1c3RlcihjbHVzdGVyaW5nLnJzdFtbMV1dLCANCiAgICAgICAgICAgICBzdGFuZD1GLCByZXBlbD0gVFJVRSwNCiAgICAgICAgICAgICBkYXRhID0gY2x1c3RlcmluZy5yc3RbWzJdXVssLTFdLCANCiAgICAgICAgICAgICB4bGFiPSIiLCB4YXh0PSJuIiwNCiAgICAgICAgICAgICBzaG93LmNsdXN0LmNlbnQ9RkFMU0UpDQoNCiMgU2Vjb25kICBUZXJtcw0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxUZXJtR3JvdXBbMl0sYygiMiIpKQ0KDQojIyBWaXN1YWx6ZSBjbHVzdGVyaW5nIHJlc3VsdHMgDQpmdml6X2NsdXN0ZXIoY2x1c3RlcmluZy5yc3RbWzFdXSwgDQogICAgICAgICAgICAgc3RhbmQ9RiwgcmVwZWw9IFRSVUUsDQogICAgICAgICAgICAgZGF0YSA9IGNsdXN0ZXJpbmcucnN0W1syXV1bLC0xXSwgDQogICAgICAgICAgICAgeGxhYj0iIiwgeGF4dD0ibiIsDQogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50PUZBTFNFKQ0KYGBgDQoNCg0KIyMjIFBvbGl0aWNhbCBQYXJ0eSAgPGEgbmFtZT0iUG9saXRpY2FsUGFydHkiPjwvYT4gIHsudGFic2V0fQ0KIyMjIyBPdmVydmlldyAgDQp8IFBvbGl0aWNhbCBQYXJ0eSB8IE51bWJlciBvZiBQcmVzaWRlbnRzIHwNCnw6LS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS0tLS06fCAgICAgDQp8IFJlcHVibGljYW4gICAgICB8IDE3ICAgICAgICAgICAgICAgICAgIHwgDQp8IERlbW9jcmF0aWMgICAgICB8IDE0ICAgICAgICAgICAgICAgICAgIHwgIA0KDQojIyMjIFN0YWdpbmcgDQpgYGB7cn0NCiMjIyBQb2xpdGljYWwgUGFydHkNCiMgU3VtbWFyeSBvZiBncm91cHMgYnkgcG9saXRpYWwgcGFydHkNCmluYXVnLmxpc3QlPiUNCiAgZ3JvdXBfYnkoUGFydHkpJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG5fZGlzdGluY3QoUHJlc2lkZW50KSkNCg0KIyBJZGVudGlmeSAnRmlsZScgZm9yIGVhY2ggZ3JvdXAgDQpEZW1vY3JhdHMgPC0gaW5hdWcubGlzdCRGaWxlW2luYXVnLmxpc3QkUGFydHk9PSJEZW1vY3JhdGljIl0NClJlcHVibGljYW5zIDwtIGluYXVnLmxpc3QkRmlsZVtpbmF1Zy5saXN0JFBhcnR5PT0iUmVwdWJsaWNhbiJdDQoNCiMgQ3JlYXRlIGdyb3VwIGFuZCBncm91cCBuYW1lDQpQYXJ0eUdyb3VwIDwtIGxpc3QoIlJlcHVibGljYW5zIj1SZXB1YmxpY2FucywgIkRlbW9jcmF0cyI9RGVtb2NyYXRzKQ0KI1BhcnR5R3JvdXAubmFtZSA8LSAiUG9saXRpY2FsIFBhcnR5Ig0KYGBgDQozMSBQT1RVUyBhcmUgYXNzaWduZWQgaW50byAyIGdyb3VwcyBieSB0aGUgcG9saXRpY2FsIHBhcnR5IHRoZXkgc2VydmVkLCBpLmUuLCBSZXB1YmxpY2FuIHZzLiBEZW1vY3JhdGljLiBUaGUgcmVzdCBvZiB0aGUgUE9UVVMgd2lsbCBub3QgYmUgaW4gc2NvcGUgb2YgdGhlIGFuYWx5c2lzIGluIHRlcm1zIG9mIHRoZSBwb2xpdGljYWwgcGFydHkgdGhleSBzZXJ2ZWQuICAgDQoNCiogICAxNyBQT1RVUyB3aG8gYXJlIFJlcHVibGljYW5zOiAgDQpgYGB7cn0NClBhcnR5R3JvdXAkUmVwdWJsaWNhbnMNCmBgYA0KDQoqICAgMTQgUE9UVVMgd2hvIGFyZSBEZW1vY3JhdHM6ICANCmBgYHtyfQ0KUGFydHlHcm91cCREZW1vY3JhdHMNCmBgYA0KIyMjIyBMZW5ndGggb2YgU3BlZWNoZXMgIA0KYGBge3IsIGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsMywyLDMpLCBtZnJvdz1jKDEsMikpDQoNCiMgQ3JlYXRlIGJhciBwbG90IG9mIGVhY2ggc3BlZWNoDQpTcGVlY2guTGVuZ3RoLkJhci5QbG90KGluYXVnLmxpc3QsUGFydHlHcm91cCkNCiMgQ3JlYXRlIGJveCBwbG90IG9mIGVhY2ggc3BlZWNoIGJ5IGdyb3VwIA0KU3BlZWNoLkxlbmd0aC5Cb3guUGxvdChpbmF1Zy5saXN0LFBhcnR5R3JvdXApDQpgYGANCiMjIyMgTGVuZ3RoIG9mIFNlbnRlbmNlcyAgIA0KYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygxLDIpKQ0KDQpCZWUuU3dhcm0uUGxvdChzZW50ZW5jZS5saXN0LFBhcnR5R3JvdXApDQpCZWUuU3dhcm0uUGxvdC5Hcm91cChzZW50ZW5jZS5saXN0LFBhcnR5R3JvdXApDQpgYGANCkJlbG93IGxpc3RlZCB0aGUgc2hvcnRlc3QgYW5kIGxvbmdlc3Qgc2VudGVuc2VzIGluIHRoZSBzcGVlY2guIA0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpTZW50ZW5jZS5TZWFyY2hlci5Hcm91cChzZW50ZW5jZS5saXN0LFBhcnR5R3JvdXAsYygiMSIsIjIiLCIzIiwiNCIpLEZBTFNFKQ0KU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAoc2VudGVuY2UubGlzdCxQYXJ0eUdyb3VwLGMoIjEiLCIyIiwiMyIsIjQiKSxUUlVFKQ0KYGBgDQoNCiMjIyMgV29yZCBDbG91ZCAgDQpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDUuNSwyLDEpLCBtZnJvdz1jKDEsbGVuZ3RoKFBhcnR5R3JvdXApKSkNCnNldC5zZWVkKDEyMykNCldvcmQuQ2xvdWQuR3JvdXAoaW5hdWcubGlzdCxQYXJ0eUdyb3VwKQ0KYGBgDQojIyMjIFNlbnRpbWVudGFsIEFuYWx5c2lzICANCkFzIHNob3duLCB0aGUgZmVhciBsZXZlbCBvZiBzcGVlY2hlcyBvZiBEZW1vY3JhdHMgaXMgaGlnaGVyIHRoYW4gdGhhdCBvZiBSZXB1YmxpY2Fucywgd2hpbGUgYWxsIG90aGVyIGVtb3Rpb25zIHNlZW1lZCB0byBiZSBhdCB0aGUgc2FtZSBsZXZlbCBhcyB0aGVtLiANCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YyhsZW5ndGgoUGFydHlHcm91cCksMSkpDQojIENyZWF0ZSBiYXJwbG90IG9mIGF2ZXJhZ2UgdmFsdWUgb2YgZW1vdGlvbnMgZm9yIGVhY2ggZ3JvdXANClNlbnRpbWVudGFsLkFuYWx5c2lzLlBsb3RzKHNlbnRlbmNlLmxpc3QsUGFydHlHcm91cCkNCmBgYA0KV2Ugd291bGQgbGlrZSB0byBsb29rIGludG8gaXQgY2xvc2VyIGFuZCB0cnkgdG8gaW5kZW50aWZ5IHRoZSBtb3N0IGluZmx1Y2VkIHNlbnRlbmNlcyB0aGF0IGRyb3ZlIHRoZSBkaWZmZXJlbmNlLiAgICANCmBgYHtyfQ0KaS5lbW8udmFyIDwtIHNlbnRlbmNlLmxpc3QlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoRmlsZSVpbiV1bmxpc3QoUGFydHlHcm91cFsxXSkpJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHNlbnRlbmNlcywgZmVhcikNCmlpLmVtby52YXIgPC0gc2VudGVuY2UubGlzdCU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihGaWxlJWluJXVubGlzdChQYXJ0eUdyb3VwWzJdKSklPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc2VudGVuY2VzLCBmZWFyKQ0KDQppLmZlYXIgPC0gaGVhZChpLmVtby52YXIlPiVhcnJhbmdlKGRlc2MoZmVhcikpJT4lc2VsZWN0KHNlbnRlbmNlcykpDQppaS5mZWFyIDwtIGhlYWQoaWkuZW1vLnZhciU+JWFycmFuZ2UoZGVzYyhmZWFyKSklPiVzZWxlY3Qoc2VudGVuY2VzKSkNCg0KaS5lbW8uZGYgPC0gY2JpbmQoaS5mZWFyLGlpLmZlYXIpDQpjb2xuYW1lcyhpLmVtby5kZikgPC0gYygiUmVwdWJsaWNhbnMgRmVhciIsIkRlbW9jcmF0cyBGZWFyIikNCmkuZW1vLmRmDQpgYGANCkJlbG93IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXMgb2YgZWFjaCBvZiB0aGVzZSB0d28gZ3JvdXBzLiAgDQpgYGB7cn0NCiMgV2hhdCBhcmUgdGhlIGVtb3Rpb25hbGx5IGNoYXJnZWQgc2VudGVuY2VzPyAgDQpTZW50aW1lbnRhbC5TZW50ZW5jZS5TZWFyY2hlcihzZW50ZW5jZS5saXN0LFBhcnR5R3JvdXAsYygiMSIsIjIiLCIzIiwiNCIpKQ0KYGBgDQpXZSBwZXJmb3JtIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcgZm9yIHRoZSB0d28gZ3JvdXBzLiBUaGUgcmVzdWx0cyBjYW4gYmUgdmlzdWFsaXplZCBhcyBzaG93biBiZWxvdy4gIA0KYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsNS41LDIsMSksIG1mcm93PWMoMiwxKSkNCiMgUmVwdXRsaWNhbg0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxQYXJ0eUdyb3VwWzFdLGMoIjEiLCIyIiwiMyIsIjQiKSkNCiMjIFZpc3VhbHplIGNsdXN0ZXJpbmcgcmVzdWx0cyANCmZ2aXpfY2x1c3RlcihjbHVzdGVyaW5nLnJzdFtbMV1dLCANCiAgICAgICAgICAgICBzdGFuZD1GLCByZXBlbD0gVFJVRSwNCiAgICAgICAgICAgICBkYXRhID0gY2x1c3RlcmluZy5yc3RbWzJdXVssLTFdLCANCiAgICAgICAgICAgICB4bGFiPSIiLCB4YXh0PSJuIiwNCiAgICAgICAgICAgICBzaG93LmNsdXN0LmNlbnQ9RkFMU0UpDQoNCiMgRGVtb2NyYXRpYw0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxQYXJ0eUdyb3VwWzJdLGMoIjEiLCIyIiwiMyIsIjQiKSkNCg0KIyMgVmlzdWFsemUgY2x1c3RlcmluZyByZXN1bHRzIA0KZnZpel9jbHVzdGVyKGNsdXN0ZXJpbmcucnN0W1sxXV0sIA0KICAgICAgICAgICAgIHN0YW5kPUYsIHJlcGVsPSBUUlVFLA0KICAgICAgICAgICAgIGRhdGEgPSBjbHVzdGVyaW5nLnJzdFtbMl1dWywtMV0sIA0KICAgICAgICAgICAgIHhsYWI9IiIsIHhheHQ9Im4iLA0KICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudD1GQUxTRSkNCmBgYA0KDQojIyMgRWR1Y2F0aW9uYWwgQmFja2dyb3VuZCA8YSBuYW1lPSJFZHVjYXRpb25hbEJhY2tncm91bmQiPjwvYT4gIHsudGFic2V0fSAgIA0KIyMjIyBPdmVydmlldyAgDQoNCnwgRWR1Y2F0aW9uYWwgQmFja2dyb3VuZCB8IE51bWJlciBvZiBQcmVzaWRlbnRzIHwNCnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tOnwgICAgIA0KfCBObyBDb2xsZWRnZSAgICAgICAgICAgIHwgOSAgICAgICAgICAgICAgICAgICAgfCANCnwgVW5kZXJncmFkdWF0ZSAgICAgICAgICB8IDIxICAgICAgICAgICAgICAgICAgIHwgIA0KfCBBZHZhbmNlZCBEZWdyZWUgICAgICAgIHwgNyAgICAgICAgICAgICAgICAgICAgfCANCg0KIyMjIyBTdGFnaW5nICANCkVkdWNhdGlvbmFsIGJhY2tnb3VuZCBvZiBQT1RVUyB3ZXJlIG9idGFpbmVkIGZyb20gdGhlIGZvbGxvd2luZyB3ZWJzaXRlczogDQoNCiogW0xpc3Qgb2YgUE9UVVMgYnkgZWR1Y2F0aW9uXVszXSAgIA0KKiBbRWR1Y2F0aW9uYWwgYW5kIENhcmVlciBCYWNrZ3JvdW5kcyBvZiBQT1RVU11bNF0gIA0KDQpgYGB7cn0NCiMjIyBFZHVjYXRpb25hbCBCYWNrZ3JvdW5kDQojIEluZGVudGlmeSBlZHVjYXRpb25hbCBiYWNrZ3JvdW5kIG9mIGVhY2ggUE9UVVMgDQpOb0NvbGxlZGdlIDwtIGMoIkdlb3JnZVdhc2hpbmd0b24iLCAiSmFtZXNNb25yb2UiLCAiQW5kcmV3SmFja3NvbiIsDQogICAgICAgICAgICAgICAgIk1hcnRpbnZhbkJ1cmVuIiwgIlphY2hhcnlUYXlsb3IiLCAiQWJyYWhhbUxpbmNvbG4iLA0KICAgICAgICAgICAgICAgICJHcm92ZXJDbGV2ZWxhbmQiLCAiV2lsbGlhbU1jS2lubGV5IiwgIkhhcnJ5U1RydW1hbiIpDQpVbmRlcmdyYWQgPC0gYygiVGhvbWFzSmVmZmVyc29uIiwiSmFtZXNNYWRpc29uIiwgIkpvaG5RdWluY3lBZGFtcyIsDQogICAgICAgICAgICAgICAiSmFtZXNLUG9sayIsICJGcmFua2xpblBpZXJjZSIsICJKYW1lc0J1Y2hhbmFuIiwgDQogICAgICAgICAgICAgICAiVWx5c3Nlc1NHcmFudCIsICJKYW1lc0dhcmZpZWxkIiwgIkJlbmphbWluSGFycmlzb24iLCANCiAgICAgICAgICAgICAgICJUaGVvZG9yZVJvb3NldmVsdCIsICJXYXJyZW5HSGFyZGluZyIsICJDYWx2aW5Db29saWRnZSIsIA0KICAgICAgICAgICAgICAgIkhlcmJlcnRIb292ZXIiLCAiRnJhbmtsaW5EUm9vc2V2ZWx0IiwgIkR3aWdodERFaXNlbmhvd2VyIiwgDQogICAgICAgICAgICAgICAiSm9obkZLZW5uZWR5IiwgIkx5bmRvbkJKb2huc29uIiwgIkppbW15Q2FydGVyIiwgDQogICAgICAgICAgICAgICAiUm9uYWxkUmVhZ2FuIiwgIkdlb3JnZUJ1c2giLCAiRG9uYWxkSlRydW1wIikNCk1BLk1TIDwtIGMoIkpvaG5BZGFtcyIpDQpNQkEgPC0gYygiR2VvcmdlV0J1c2giKQ0KTGF3IDwtIGMoIlJ1dGhlcmZvcmRCSGF5ZXMiLCAiUmljaGFyZE5peG9uIiwgIldpbGxpYW1KQ2xpbnRvbiIsICJCYXJhY2tPYmFtYSIpDQpEb2N0b3JhdGUgPC0gYygiV29vZHJvd1dpbHNvbiIpDQoNCkFkdmFuY2VkLkRlZ3JlZSA8LSBjKE1BLk1TLCBNQkEsIExhdywgRG9jdG9yYXRlKSANCg0KIyBHcm91cCBQT1RVUyBpbnRvIHRocmVlIGNhdGVnb3JpZXMgYW5kIENyZWF0ZSBncm91cCBhbmQgZ3JvdXAgbmFtZQ0KRWR1Y2F0aW9uR3JvdXAgPC0gbGlzdCgiTm8gQ29sbGVkZ2UiPU5vQ29sbGVkZ2UsDQogICAgICAgICAgICAgICAgICAgICAgICAiVW5kZXJncmFkdWF0ZSI9VW5kZXJncmFkLA0KICAgICAgICAgICAgICAgICAgICAgICAgIkFkdmFuY2VkIERlZ3JlZSI9QWR2YW5jZWQuRGVncmVlKQ0KI0VkdWNhdGlvbkdyb3VwLm5hbWUgPC0gIkVkdWNhdGlvbmFsIEJhY2tncm91bmQiDQoNCiNzdW1tYXJ5KEVkdWNhdGlvbkdyb3VwKVssMV0NCmBgYA0KUE9UVVMgYXJlIGFzc2lnbmVkIGludG8gdGhyZWUgZ3JvdXBzIGJhc2VkIG9uIHRoZWlyIGhpZ2hlc3QgZWR1Y2F0aW9uIGxldmVsLiANCg0KKiAgYHIgbGVuZ3RoKE5vQ29sbGVkZ2UpYCBQT1RVUyB3aXRob3V0IGEgY29sbGVkZ2UgZGVncmVlIGluY2x1ZGU6IA0KYGBge3J9DQpFZHVjYXRpb25Hcm91cCRgTm8gQ29sbGVkZ2VgDQpgYGANCiogIGByIGxlbmd0aChVbmRlcmdyYWQpYCBQT1RVUyB3aXRoIG9ubHkgdW5kZXJncmFkdWF0ZSBkZWdyZWUgaW5jbHVkZTogDQpgYGB7cn0NCkVkdWNhdGlvbkdyb3VwJFVuZGVyZ3JhZHVhdGUNCmBgYA0KKiAgYHIgbGVuZ3RoKEFkdmFuY2VkLkRlZ3JlZSlgIFBPVFVTIHdpdGggbW9yZSBhZHZhbmNlZCBkZWdyZWUgaW5jbHVkZTogDQpgYGB7cn0NCkVkdWNhdGlvbkdyb3VwJGBBZHZhbmNlZCBEZWdyZWVgDQpgYGANCg0KIyMjIyBMZW5ndGggb2YgU3BlZWNoZXMgIA0KYGBge3IsIGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsMywyLDMpLCBtZnJvdz1jKDEsMikpDQoNCiMgQ3JlYXRlIGJhciBwbG90IG9mIGVhY2ggc3BlZWNoDQpTcGVlY2guTGVuZ3RoLkJhci5QbG90KGluYXVnLmxpc3QsRWR1Y2F0aW9uR3JvdXApDQojIENyZWF0ZSBib3ggcGxvdCBvZiBlYWNoIHNwZWVjaCBieSBncm91cCANClNwZWVjaC5MZW5ndGguQm94LlBsb3QoaW5hdWcubGlzdCxFZHVjYXRpb25Hcm91cCkNCmBgYA0KIyMjIyBMZW5ndGggb2YgU2VudGVuY2VzIA0KYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygxLDIpKQ0KDQpCZWUuU3dhcm0uUGxvdChzZW50ZW5jZS5saXN0LEVkdWNhdGlvbkdyb3VwKQ0KQmVlLlN3YXJtLlBsb3QuR3JvdXAoc2VudGVuY2UubGlzdCxFZHVjYXRpb25Hcm91cCkNCmBgYA0KQmVsb3cgbGlzdGVkIHRoZSBzaG9ydGVzdCBhbmQgbG9uZ2VzdCBzZW50ZW5zZXMgaW4gdGhlIHNwZWVjaC4gDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NClNlbnRlbmNlLlNlYXJjaGVyLkdyb3VwKHNlbnRlbmNlLmxpc3QsRWR1Y2F0aW9uR3JvdXAsYygiMSIsIjIiLCIzIiwiNCIpLEZBTFNFKQ0KU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAoc2VudGVuY2UubGlzdCxFZHVjYXRpb25Hcm91cCxjKCIxIiwiMiIsIjMiLCI0IiksVFJVRSkNCmBgYA0KIyMjIyBXb3JkIENsb3VkICANCmBgYHtyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDQuNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsNS41LDIsMSksIG1mcm93PWMoMSxsZW5ndGgoRWR1Y2F0aW9uR3JvdXApKSkNCnNldC5zZWVkKDEyMykNCg0KV29yZC5DbG91ZC5Hcm91cChpbmF1Zy5saXN0LEVkdWNhdGlvbkdyb3VwKQ0KYGBgDQoNCiMjIyMgU2VudGltZW50YWwgQW5hbHlzaXMgIA0KYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA0LjUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDUuNSwyLDEpLCBtZnJvdz1jKGxlbmd0aChFZHVjYXRpb25Hcm91cCksMSkpDQojIENyZWF0ZSBiYXJwbG90IG9mIGF2ZXJhZ2UgdmFsdWUgb2YgZW1vdGlvbnMgZm9yIGVhY2ggZ3JvdXANClNlbnRpbWVudGFsLkFuYWx5c2lzLlBsb3RzKHNlbnRlbmNlLmxpc3QsRWR1Y2F0aW9uR3JvdXApDQpgYGANCkJlbG93IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXMgb2YgZWFjaCBvZiB0aGVzZSB0d28gZ3JvdXBzLiAgIA0KYGBge3J9DQojIFdoYXQgYXJlIHRoZSBlbW90aW9uYWxseSBjaGFyZ2VkIHNlbnRlbmNlcz8gIA0KU2VudGltZW50YWwuU2VudGVuY2UuU2VhcmNoZXIoc2VudGVuY2UubGlzdCxFZHVjYXRpb25Hcm91cCxjKCIxIiwiMiIsIjMiLCI0IikpDQpgYGANCldlIHBlcmZvcm1lZCB0aGUgay1tZWFucyBjbHVzdGVyaW5nIGZvciB0aGUgdHdvIGdyb3Vwcy4gVGhlIHJlc3VsdHMgY2FuIGJlIHZpc3VhbGl6ZWQgYXMgc2hvd24gYmVsb3cuICANCmBgYHtyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDEwLCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygyLDEpKQ0KIyBObyBDb2xsZWRnZSBEZWdyZWUgDQojIyBQZXJmb3JtIGstbWVhbnMgY2x1c3RlcmluZyANCmNsdXN0ZXJpbmcucnN0IDwtIFNlbnRpbWVudGFsLlNlbnRlbmNlLktNZWFucyhzZW50ZW5jZS5saXN0LEVkdWNhdGlvbkdyb3VwWzFdLGMoIjEiLCIyIiwiMyIsIjQiKSkNCiMjIFZpc3VhbHplIGNsdXN0ZXJpbmcgcmVzdWx0cyANCmZ2aXpfY2x1c3RlcihjbHVzdGVyaW5nLnJzdFtbMV1dLCANCiAgICAgICAgICAgICBzdGFuZD1GLCByZXBlbD0gVFJVRSwNCiAgICAgICAgICAgICBkYXRhID0gY2x1c3RlcmluZy5yc3RbWzJdXVssLTFdLCANCiAgICAgICAgICAgICB4bGFiPSIiLCB4YXh0PSJuIiwNCiAgICAgICAgICAgICBzaG93LmNsdXN0LmNlbnQ9RkFMU0UpDQoNCiMgVW5kZXJncmFkdWF0ZSBEZWdyZWUgT25seQ0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxFZHVjYXRpb25Hcm91cFsyXSxjKCIxIiwiMiIsIjMiLCI0IikpDQojIyBWaXN1YWx6ZSBjbHVzdGVyaW5nIHJlc3VsdHMgDQpmdml6X2NsdXN0ZXIoY2x1c3RlcmluZy5yc3RbWzFdXSwgDQogICAgICAgICAgICAgc3RhbmQ9RiwgcmVwZWw9IFRSVUUsDQogICAgICAgICAgICAgZGF0YSA9IGNsdXN0ZXJpbmcucnN0W1syXV1bLC0xXSwgDQogICAgICAgICAgICAgeGxhYj0iIiwgeGF4dD0ibiIsDQogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50PUZBTFNFKQ0KDQojIEFkdmVuY2VkIERlZ3JlZQ0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxFZHVjYXRpb25Hcm91cFszXSxjKCIxIiwiMiIsIjMiLCI0IikpDQojIyBWaXN1YWx6ZSBjbHVzdGVyaW5nIHJlc3VsdHMgDQpmdml6X2NsdXN0ZXIoY2x1c3RlcmluZy5yc3RbWzFdXSwgDQogICAgICAgICAgICAgc3RhbmQ9RiwgcmVwZWw9IFRSVUUsDQogICAgICAgICAgICAgZGF0YSA9IGNsdXN0ZXJpbmcucnN0W1syXV1bLC0xXSwgDQogICAgICAgICAgICAgeGxhYj0iIiwgeGF4dD0ibiIsDQogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50PUZBTFNFKQ0KDQpgYGANCg0KIyMjIENhcmVlciBQcmlvciB0byBQb2xpdGljcyAgPGEgbmFtZT0iQ2FyZWVyQmFja2dyb3VuZCI+PC9hPiAgey50YWJzZXR9DQojIyMjIE92ZXJ2aWV3ICANCg0KfCBDYXJlZXIgUHJpb3IgdG8gUG9saXRpY3MgfCBOdW1iZXIgb2YgUHJlc2lkZW50cyB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tOnwgICAgIA0KfCBMYXd5ZXIgICAgICAgICAgICAgICAgICAgfCAyMSAgICAgICAgICAgICAgICAgICB8IA0KfCBNaWxpdGFyeSBMZWFkZXIgICAgICAgICAgfCAzICAgICAgICAgICAgICAgICAgICB8ICANCnwgRmFybWVyICAgICAgICAgICAgICAgICAgIHwgMyAgICAgICAgICAgICAgICAgICAgfCANCnwgRWR1Y2F0b3IgICAgICAgICAgICAgICAgIHwgMiAgICAgICAgICAgICAgICAgICAgfCAgDQp8IEJ1c2luZXNzcGVyc29uICAgICAgICAgICB8IDMgICAgICAgICAgICAgICAgICAgIHwgDQoNCiMjIyMgU3RhZ2luZyAgDQpDYXJlZXIgYmFja2dvdW5kIG9mIFBPVFVTIHdlcmUgb2J0YWluZWQgZnJvbSB0aGUgZm9sbG93aW5nIHdlYnNpdGU6IA0KDQoqIFtFZHVjYXRpb25hbCBhbmQgQ2FyZWVyIEJhY2tncm91bmRzIG9mIFBPVFVTXVs0XSAgDQoNCmBgYHtyfQ0KIyMjIENhcmVlciBQcmlvciB0byBQb2xpdGljcyAgDQojIEluZGVudGlmeSBjYXJlZXIgYmFja2dyb3VuZCBvZiBlYWNoIFBPVFVTIA0KbGF3eWVyIDwtIGMoIkpvaG5BZGFtcyIsICJUaG9tYXNKZWZmZXJzb24iLCAiSmFtZXNNYWRpc29uIiwgDQogICAgICAgICAgICAiSmFtZXNNb25yb2UiLCAiSm9oblF1aW5jeUFkYW1zIiwgIkFuZHJld0phY2tzb24iLCANCiAgICAgICAgICAgICJNYXJ0aW52YW5CdXJlbiIsICJKYW1lc0tQb2xrIiwgIkZyYW5rbGluUGllcmNlIiwgDQogICAgICAgICAgICAiSmFtZXNCdWNoYW5hbiIsICJBYnJhaGFtTGluY29sbiIsICJSdXRoZXJmb3JkQkhheWVzIiwgDQogICAgICAgICAgICAiSmFtZXNHYXJmaWVsZCIsICJHcm92ZXJDbGV2ZWxhbmQiLCAiQmVuamFtaW5IYXJyaXNvbiIsIA0KICAgICAgICAgICAgIldpbGxpYW1NY0tpbmxleSIsICJDYWx2aW5Db29saWRnZSIsICJGcmFua2xpbkRSb29zZXZlbHQiLCANCiAgICAgICAgICAgICJSaWNoYXJkTml4b24iLCAiV2lsbGlhbUpDbGludG9uIiwgIkJhcmFja09iYW1hIikNCm1pbGl0YXJ5LmxlYWRlciA8LSBjKCJaYWNoYXJ5VGF5bG9yIiwgIlVseXNzZXNTR3JhbnQiLCAiRHdpZ2h0REVpc2VuaG93ZXIiKQ0KZmFybWVyIDwtIGMoIkdlb3JnZVdhc2hpbmd0b24iLCAiSGFycnlTVHJ1bWFuIiwgIkppbW15Q2FydGVyIikNCmJ1c2luZXNzcGVyc29uIDwtIGMoIkdlb3JnZUJ1c2giLCAiR2VvcmdlV0J1c2giLCAiRG9uYWxkSlRydW1wIikNCmVkdWNhdG9yIDwtIGMoIldvb2Ryb3dXaWxzb24iLCAiTHluZG9uQkpvaG5zb24iKQ0KDQojIEdyb3VwIFBPVFVTIGludG8gZml2ZSBjYXRlZ29yaWVzIGFuZCBDcmVhdGUgZ3JvdXAgYW5kIGdyb3VwIG5hbWUNCkNhcmVlckdyb3VwIDwtIGxpc3QoIkxhd3llciI9bGF3eWVyLCANCiAgICAgICAgICAgICAgICJNaWxpdGFyeSBMZWFyZGVyIj1taWxpdGFyeS5sZWFkZXIsDQogICAgICAgICAgICAgICAiRmFybWVyIj1mYXJtZXIsDQogICAgICAgICAgICAgICAiRWR1Y2F0b3IiPWVkdWNhdG9yLCANCiAgICAgICAgICAgICAgICJCdXNpbmVzc3BlcnNvbiI9YnVzaW5lc3NwZXJzb24pDQojQ2FyZWVyR3JvdXAubmFtZSA8LSAiQ2FyZWVyIFByaW9yIHRvIFBvbGl0aWNzIg0KI3N1bW1hcnkoQ2FyZWVyR3JvdXApWywxXQ0KYGBgDQoNClBPVFVTIGFyZSBhc3NpZ25lZCBpbnRvIGZpdmUgZ3JvdXBzIGJhc2VkIG9uIHRoZWlyIGNhcmVlciBwaW9yIHRvIHBvbGl0aWNzLiANCg0KKiAgIGByIGxlbmd0aChsYXd5ZXIpYCBQT1RVUyB3ZXJlIGxhd3l3ZXJzIGluY2x1ZGU6IA0KYGBge3J9DQpDYXJlZXJHcm91cCRMYXd5ZXINCmBgYA0KKiAgYHIgbGVuZ3RoKG1pbGl0YXJ5LmxlYWRlcilgIFBPVFVTIHdlcmUgbWlsaXRhcnkgbGVhZGVyczogDQpgYGB7cn0NCkNhcmVlckdyb3VwJGBNaWxpdGFyeSBMZWFyZGVyYA0KYGBgDQoqICBgciBsZW5ndGgoZmFybWVyKWAgUE9UVVMgd2VyZSBmYXJtZXJzOiANCmBgYHtyfQ0KQ2FyZWVyR3JvdXAkRmFybWVyDQpgYGANCiogIGByIGxlbmd0aChlZHVjYXRvcilgIFBPVFVTIHdlcmUgZWR1Y2F0b3JzOiANCmBgYHtyfQ0KQ2FyZWVyR3JvdXAkRWR1Y2F0b3INCmBgYA0KKiAgYHIgbGVuZ3RoKGJ1c2luZXNzcGVyc29uKWAgUE9UVVMgd2VyZSBidXNpbmVzc3BlcnNvbnM6IA0KYGBge3J9DQpDYXJlZXJHcm91cCRCdXNpbmVzc3BlcnNvbg0KYGBgDQoNCiMjIyMgTGVuZ3RoIG9mIFNwZWVjaGVzICANCmBgYHtyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDMsMiwzKSwgbWZyb3c9YygxLDIpKQ0KDQojIENyZWF0ZSBiYXIgcGxvdCBvZiBlYWNoIHNwZWVjaA0KU3BlZWNoLkxlbmd0aC5CYXIuUGxvdChpbmF1Zy5saXN0LENhcmVlckdyb3VwKQ0KIyBDcmVhdGUgYm94IHBsb3Qgb2YgZWFjaCBzcGVlY2ggYnkgZ3JvdXAgDQpTcGVlY2guTGVuZ3RoLkJveC5QbG90KGluYXVnLmxpc3QsQ2FyZWVyR3JvdXApDQpgYGANCiMjIyMgTGVuZ3RoIG9mIFNlbnRlbmNlcyANCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDQuNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsNS41LDIsMSksIG1mcm93PWMoMSwyKSkNCg0KQmVlLlN3YXJtLlBsb3Qoc2VudGVuY2UubGlzdCxDYXJlZXJHcm91cCkNCkJlZS5Td2FybS5QbG90Lkdyb3VwKHNlbnRlbmNlLmxpc3QsQ2FyZWVyR3JvdXApDQpgYGANCkJlbG93IGxpc3RlZCB0aGUgc2hvcnRlc3QgYW5kIGxvbmdlc3Qgc2VudGVuc2VzIGluIHRoZSBzcGVlY2guIA0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpTZW50ZW5jZS5TZWFyY2hlci5Hcm91cChzZW50ZW5jZS5saXN0LENhcmVlckdyb3VwLGMoIjEiLCIyIiwiMyIsIjQiKSxGQUxTRSkNClNlbnRlbmNlLlNlYXJjaGVyLkdyb3VwKHNlbnRlbmNlLmxpc3QsQ2FyZWVyR3JvdXAsYygiMSIsIjIiLCIzIiwiNCIpLFRSVUUpDQpgYGANCiMjIyMgV29yZCBDbG91ZCAgDQpgYGB7ciwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA2LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygxLGxlbmd0aChDYXJlZXJHcm91cCkpKQ0Kc2V0LnNlZWQoMTIzKQ0KDQpXb3JkLkNsb3VkLkdyb3VwKGluYXVnLmxpc3QsQ2FyZWVyR3JvdXApDQpgYGANCg0KIyMjIyBTZW50aW1lbnRhbCBBbmFseXNpcyAgDQpgYGB7ciwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDEwLCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YyhsZW5ndGgoQ2FyZWVyR3JvdXApLDEpKQ0KIyBDcmVhdGUgYmFycGxvdCBvZiBhdmVyYWdlIHZhbHVlIG9mIGVtb3Rpb25zIGZvciBlYWNoIGdyb3VwDQpTZW50aW1lbnRhbC5BbmFseXNpcy5QbG90cyhzZW50ZW5jZS5saXN0LENhcmVlckdyb3VwKQ0KYGBgDQoNCkJlbG93IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXMgb2YgZWFjaCBvZiB0aGVzZSBncm91cHMuICAgDQpgYGB7cn0NCiMgV2hhdCBhcmUgdGhlIGVtb3Rpb25hbGx5IGNoYXJnZWQgc2VudGVuY2VzPyAgDQpTZW50aW1lbnRhbC5TZW50ZW5jZS5TZWFyY2hlcihzZW50ZW5jZS5saXN0LENhcmVlckdyb3VwLGMoIjEiLCIyIiwiMyIsIjQiKSkNCmBgYA0KV2UgcGVyZm9ybWVkIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcgb2YgdGhlIExhd3llciBncm91cCwgYXMgc2l6ZSBvZiBvdGhlciBncm91cHMgYXJlIHF1aXRlIHNtYWxsLiANCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDUsIHdhcm5pbmc9RkFMU0V9DQpwYXIobWFyPWMoNC41LDUuNSwyLDEpLCBtZnJvdz1jKDIsMSkpDQojIExhd3llcg0KIyMgUGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcgDQpjbHVzdGVyaW5nLnJzdCA8LSBTZW50aW1lbnRhbC5TZW50ZW5jZS5LTWVhbnMoc2VudGVuY2UubGlzdCxDYXJlZXJHcm91cFsxXSxjKCIxIiwiMiIsIjMiLCI0IikpDQojIyBWaXN1YWx6ZSBjbHVzdGVyaW5nIHJlc3VsdHMgDQpmdml6X2NsdXN0ZXIoY2x1c3RlcmluZy5yc3RbWzFdXSwgDQogICAgICAgICAgICAgc3RhbmQ9RiwgcmVwZWw9IFRSVUUsDQogICAgICAgICAgICAgZGF0YSA9IGNsdXN0ZXJpbmcucnN0W1syXV1bLC0xXSwgDQogICAgICAgICAgICAgeGxhYj0iIiwgeGF4dD0ibiIsDQogICAgICAgICAgICAgc2hvdy5jbHVzdC5jZW50PUZBTFNFKQ0KYGBgDQoNCiMjIyBTZXJ2ZWQgRHVyaW5nIFdhciBFcmE/ICA8YSBuYW1lPSJXYXJFcmEiPjwvYT4gIHsudGFic2V0fSAgICAgDQojIyMjIE92ZXJ2aWV3ICANCg0KfCBTZXJ2ZWQgRHVyaW5nIFdhciBFcmE/IHwgTnVtYmVyIG9mIFByZXNpZGVudHMgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS0tLS06fCAgICAgDQp8IE5vIFdhciBFcmEgICAgICAgICAgICAgfCAzOCAgICAgICAgICAgICAgICAgICB8IA0KfCBXYXIgRXJhICAgICAgICAgICAgICAgIHwgMTEgICAgICAgICAgICAgICAgICAgfCAgDQoNCiMjIyMgU3RhZ2luZyAgDQpUaGUgZm9sbG93aW5nIHdlYnNpdGUgd2FzIHVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdW5kZXJzdGFuZCBtb3JlIGFib3V0IHRoZSBVLlMuIGhpc3RvcnkuIEl0IHByZXNlbnRzIGEgdGltZWxpbmUgb2YgdGhlIFUuUy4gaGl0b3J5LCB3aGljaCBzcGVjaWZpZXMgdGhlIHdhcnMgdGhlIFUuUy4gd2FzIGludm9sZGVkIGluLiANCg0KKiBbQW5pbWF0ZWQgQXRsYXNdWzVdDQoNClBPVFVTIGFyZSBhc3NpZ25lZCBpbnRvIHR3byBncm91cHMgYmFzZWQgb24gd2hldGhlciBpdCB3YXMgd2FyIGVyYSB3aGlsZSB0aGV5IHdlcmUgc2VydmluZy4gDQoNCiogICAxMSBQT1RVUyB3aG8gc2VydmVkIGR1cmluZyB3YXIgZXJhcyBpbmNsdWRlOiANCkphbWVzIE1hZGlzb24oV2FyIG9mIDE4MTIpLCBKYW1lcyBQb2xrKE1leGljYW4gV2FyKSwgQWJyYWhhbSBMaW5jb2xuKENpdmlsIFdhciksIFdpbGxpYW0gTWNLaW5sZXkoU3BhbmlzaC1BbWVyaWNhbiBXYXIpLCBXb29kcm93IFdpbHNvbihXV0kpLCBGcmFua2xpbiBELiBSb29zZXZlbHQoV1dJSSksIEhhcnJ5IFMuIFRydW1hbihLb3JlYW4gV2FyKSwgTHluZG9uIEpvaG5zb24oVmlldGFtIFdhciksIFJpY2hhcmQgTml4b24oVmlldG5hbSBXYXIpLCBHZW9yZ2UgQnVzaChHdWxmIFdhciksIEdlb3JnZSBXLiBCdXNoKFdhciBvbiBUZXJyb3IpLiAgDQoNCiogICBUaGUgcmVzdCAzOCBQT1RVUyB3ZXJlIHNlcnZpbmcgbm90IGR1cmluZyB3YXIgZXJhLg0KYGBge3J9DQojIyMgU2VydmVkIER1cmluZyBXYXIgRXJhPyAgDQojIEluZGVudGlmeSBQT1RVUyB3aG8gc2VydmVkIGR1cmluZyB3YXIgZXJhcyANCndhci5lcmEgPC0gYygiSmFtZXNNYWRpc29uIiwgIkphbWVzS1BvbGsiLCAiQWJyYWhhbUxpbmNvbG4iLCANCiAgICAgICAgICAgICAiV2lsbGlhbU1jS2lubGV5IiwgIldvb2Ryb3dXaWxzb24iLCAiRnJhbmtsaW5EUm9vc2V2ZWx0IiwNCiAgICAgICAgICAgICAiSGFycnlTVHJ1bWFuIiwgIkx5bmRvbkJKb2huc29uIiwgIlJpY2hhcmROaXhvbiIsDQogICAgICAgICAgICAgIkdlb3JnZUJ1c2giLCAiR2VvcmdlV0J1c2giKQ0Kbm93YXIuZXJhIDwtIGluYXVnLmxpc3QkRmlsZVshKGluYXVnLmxpc3QkRmlsZSVpbiV3YXIuZXJhKV0gDQoNCiMgQ3JlYXRlIGdyb3VwIGFuZCBncm91cCBuYW1lDQpXYXJHcm91cCA8LSBsaXN0KCJObyBXYXIgRXJhIj1ub3dhci5lcmEsICJXYXIgRXJhIj13YXIuZXJhKQ0KI1dhckdyb3VwLm5hbWUgPC0gIldoZXRoZXIgU2VydmVkIER1cmluZyBXYXIgRXJhIiANCg0Kc3VtbWFyeShXYXJHcm91cClbLDFdDQpgYGANCg0KIyMjIyBMZW5ndGggb2YgU3BlZWNoZXMgIA0KYGBge3IsIGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZz1GQUxTRX0NCnBhcihtYXI9Yyg0LjUsMywyLDMpLCBtZnJvdz1jKDEsMikpDQoNCiMgQ3JlYXRlIGJhciBwbG90IG9mIGVhY2ggc3BlZWNoDQpTcGVlY2guTGVuZ3RoLkJhci5QbG90KGluYXVnLmxpc3QsV2FyR3JvdXApDQojIENyZWF0ZSBib3ggcGxvdCBvZiBlYWNoIHNwZWVjaCBieSBncm91cCANClNwZWVjaC5MZW5ndGguQm94LlBsb3QoaW5hdWcubGlzdCxXYXJHcm91cCkNCmBgYA0KIyMjIyBMZW5ndGggb2YgU2VudGVuY2VzIA0KYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygxLDIpKQ0KDQpCZWUuU3dhcm0uUGxvdChzZW50ZW5jZS5saXN0LFdhckdyb3VwKQ0KQmVlLlN3YXJtLlBsb3QuR3JvdXAoc2VudGVuY2UubGlzdCxXYXJHcm91cCkNCmBgYA0KQmVsb3cgbGlzdGVkIHRoZSBzaG9ydGVzdCBhbmQgbG9uZ2VzdCBzZW50ZW5zZXMgaW4gdGhlIHNwZWVjaC4gDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NClNlbnRlbmNlLlNlYXJjaGVyLkdyb3VwKHNlbnRlbmNlLmxpc3QsV2FyR3JvdXAsYygiMSIsIjIiLCIzIiwiNCIpLEZBTFNFKQ0KU2VudGVuY2UuU2VhcmNoZXIuR3JvdXAoc2VudGVuY2UubGlzdCxXYXJHcm91cCxjKCIxIiwiMiIsIjMiLCI0IiksVFJVRSkNCmBgYA0KIyMjIyBXb3JkIENsb3VkICANCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygxLGxlbmd0aChXYXJHcm91cCkpKQ0Kc2V0LnNlZWQoMTIzKQ0KDQpXb3JkLkNsb3VkLkdyb3VwKGluYXVnLmxpc3QsV2FyR3JvdXApDQpgYGANCg0KIyMjIyBTZW50aW1lbnRhbCBBbmFseXNpcyAgDQoNCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNC41LCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YyhsZW5ndGgoV2FyR3JvdXApLDEpKQ0KIyBDcmVhdGUgYmFycGxvdCBvZiBhdmVyYWdlIHZhbHVlIG9mIGVtb3Rpb25zIGZvciBlYWNoIGdyb3VwDQpTZW50aW1lbnRhbC5BbmFseXNpcy5QbG90cyhzZW50ZW5jZS5saXN0LFdhckdyb3VwKQ0KYGBgDQoNCkJlbG93IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXMgb2YgZWFjaCBvZiB0aGVzZSB0d28gZ3JvdXBzLiAgIA0KYGBge3J9DQojIFdoYXQgYXJlIHRoZSBlbW90aW9uYWxseSBjaGFyZ2VkIHNlbnRlbmNlcz8gIA0KU2VudGltZW50YWwuU2VudGVuY2UuU2VhcmNoZXIoc2VudGVuY2UubGlzdCxXYXJHcm91cCxjKCIxIiwiMiIsIjMiLCI0IikpDQpgYGANCldlIHBlcmZvcm1lZCB0aGUgay1tZWFucyBjbHVzdGVyaW5nIGZvciB0aGUgdHdvIGdyb3Vwcy4gVGhlIHJlc3VsdHMgY2FuIGJlIHZpc3VhbGl6ZWQgYXMgc2hvd24gYmVsb3cuICANCmBgYHtyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDEwLCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1hcj1jKDQuNSw1LjUsMiwxKSwgbWZyb3c9YygyLDEpKQ0KIyBObyBXYXIgRXJhDQojIyBQZXJmb3JtIGstbWVhbnMgY2x1c3RlcmluZyANCmNsdXN0ZXJpbmcucnN0IDwtIFNlbnRpbWVudGFsLlNlbnRlbmNlLktNZWFucyhzZW50ZW5jZS5saXN0LFdhckdyb3VwWzFdLGMoIjEiLCIyIiwiMyIsIjQiKSkNCiMjIFZpc3VhbHplIGNsdXN0ZXJpbmcgcmVzdWx0cyANCmZ2aXpfY2x1c3RlcihjbHVzdGVyaW5nLnJzdFtbMV1dLCANCiAgICAgICAgICAgICBzdGFuZD1GLCByZXBlbD0gVFJVRSwNCiAgICAgICAgICAgICBkYXRhID0gY2x1c3RlcmluZy5yc3RbWzJdXVssLTFdLCANCiAgICAgICAgICAgICB4bGFiPSIiLCB4YXh0PSJuIiwNCiAgICAgICAgICAgICBzaG93LmNsdXN0LmNlbnQ9RkFMU0UpDQojIFdhciBFcmENCiMjIFBlcmZvcm0gay1tZWFucyBjbHVzdGVyaW5nIA0KY2x1c3RlcmluZy5yc3QgPC0gU2VudGltZW50YWwuU2VudGVuY2UuS01lYW5zKHNlbnRlbmNlLmxpc3QsV2FyR3JvdXBbMl0sYygiMSIsIjIiLCIzIiwiNCIpKQ0KIyMgVmlzdWFsemUgY2x1c3RlcmluZyByZXN1bHRzIA0KZnZpel9jbHVzdGVyKGNsdXN0ZXJpbmcucnN0W1sxXV0sIA0KICAgICAgICAgICAgIHN0YW5kPUYsIHJlcGVsPSBUUlVFLA0KICAgICAgICAgICAgIGRhdGEgPSBjbHVzdGVyaW5nLnJzdFtbMl1dWywtMV0sIA0KICAgICAgICAgICAgIHhsYWI9IiIsIHhheHQ9Im4iLA0KICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudD1GQUxTRSkNCmBgYA0KDQojIyBSZWZlcmVuY2VzICANCg0KKiBbVGhlIEFtZXJpY2FuIFByZXNpZGVudGN5IFByb2plY3RdWzFdDQoqIFtMaXN0IG9mIFBPVFVTXVsyXSAgIA0KKiBbTGlzdCBvZiBQT1RVUyBieSBlZHVjYXRpb25dWzNdICAgDQoqIFtFZHVjYXRpb25hbCBhbmQgQ2FyZWVyIEJhY2tncm91bmRzIG9mIFBPVFVTXVs0XSAgDQoqIFtBbmltYXRlZCBBdGxhc11bNV0NCiogW0FEUyBHaXRodWJdWzZdDQoNClsxXTogaHR0cDovL3d3dy5wcmVzaWRlbmN5LnVjc2IuZWR1L2luYXVndXJhbHMucGhwDQpbMl06IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfUHJlc2lkZW50c19vZl90aGVfVW5pdGVkX1N0YXRlcw0KWzNdOiBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaXN0X29mX1ByZXNpZGVudHNfb2ZfdGhlX1VuaXRlZF9TdGF0ZXNfYnlfZWR1Y2F0aW9uDQpbNF06IGh0dHBzOi8vaWpyLmNvbS8yMDE1LzA3LzM2NjY5NC1jaGFydHMtc2hvdy1lZHVjYXRpb25hbC13b3JrLWJhY2tncm91bmRzLXByZXNpZGVudGlhbC1jYW5kaWRhdGVzLyANCls1XTogaHR0cDovL3d3dy5hbmltYXRlZGF0bGFzLmNvbS90aW1lbGluZS5odG1sDQpbNl06aHR0cHM6Ly9naXRodWIuY29tL1Rac3RhdHNBRFMvQURTX1RlYWNoaW5nL3RyZWUvbWFzdGVyL1R1dG9yaWFscy93azItVGV4dE1pbmluZw0KDQoNCg==